Merge pull request #107 from RaidMax/feature/issue-104-allow-per-command-permission-config

Feature/issue 104 allow per command permission config
This commit is contained in:
RaidMax 2020-01-31 20:23:37 -06:00 committed by GitHub
commit c6d6bebeab
48 changed files with 1397 additions and 763 deletions

View File

@ -9,7 +9,6 @@ using SharedLibraryCore.Configuration.Validation;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Events;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
@ -44,12 +43,12 @@ namespace IW4MAdmin.Application
public string ExternalIPAddress { get; private set; }
public bool IsRestartRequested { get; private set; }
public IMiddlewareActionHandler MiddlewareActionHandler { get; }
private readonly List<Command> Commands;
private readonly List<IManagerCommand> _commands;
private readonly List<MessageToken> MessageTokens;
private readonly ClientService ClientSvc;
readonly AliasService AliasSvc;
readonly PenaltyService PenaltySvc;
public BaseConfigurationHandler<ApplicationConfiguration> ConfigHandler;
public IConfigurationHandler<ApplicationConfiguration> ConfigHandler;
GameEventHandler Handler;
readonly IPageList PageList;
private readonly Dictionary<long, ILogger> _loggers = new Dictionary<long, ILogger>();
@ -57,17 +56,21 @@ namespace IW4MAdmin.Application
private readonly TimeSpan _throttleTimeout = new TimeSpan(0, 1, 0);
private readonly CancellationTokenSource _tokenSource;
private readonly Dictionary<string, Task<IList>> _operationLookup = new Dictionary<string, Task<IList>>();
private readonly ITranslationLookup _translationLookup;
private readonly IConfigurationHandler<CommandConfiguration> _commandConfiguration;
private readonly IPluginImporter _pluginImporter;
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler)
public ApplicationManager(ILogger logger, IMiddlewareActionHandler actionHandler, IEnumerable<IManagerCommand> commands,
ITranslationLookup translationLookup, IConfigurationHandler<CommandConfiguration> commandConfiguration,
IConfigurationHandler<ApplicationConfiguration> appConfigHandler, IPluginImporter pluginImporter)
{
MiddlewareActionHandler = actionHandler;
_servers = new ConcurrentBag<Server>();
Commands = new List<Command>();
MessageTokens = new List<MessageToken>();
ClientSvc = new ClientService();
AliasSvc = new AliasService();
PenaltySvc = new PenaltyService();
ConfigHandler = new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings");
ConfigHandler = appConfigHandler;
StartTime = DateTime.UtcNow;
PageList = new PageList();
AdditionalEventParsers = new List<IEventParser>();
@ -76,6 +79,10 @@ namespace IW4MAdmin.Application
_metaService = new MetaService();
_tokenSource = new CancellationTokenSource();
_loggers.Add(0, logger);
_commands = commands.ToList();
_translationLookup = translationLookup;
_commandConfiguration = commandConfiguration;
_pluginImporter = pluginImporter;
}
public async Task ExecuteEvent(GameEvent newEvent)
@ -153,9 +160,9 @@ namespace IW4MAdmin.Application
return Servers;
}
public IList<Command> GetCommands()
public IList<IManagerCommand> GetCommands()
{
return Commands;
return _commands;
}
public async Task UpdateServerStates()
@ -242,18 +249,21 @@ namespace IW4MAdmin.Application
ExternalIPAddress = await Utilities.GetExternalIP();
#region PLUGINS
SharedLibraryCore.Plugins.PluginImporter.Load(this);
foreach (var Plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
foreach (var plugin in _pluginImporter.ActivePlugins)
{
try
{
await Plugin.OnLoadAsync(this);
if (plugin is ScriptPlugin scriptPlugin)
{
await scriptPlugin.Initialize(this);
}
await plugin.OnLoadAsync(this);
}
catch (Exception ex)
{
Logger.WriteError($"{Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_PLUGIN"]} {Plugin.Name}");
Logger.WriteError($"{_translationLookup["SERVER_ERROR_PLUGIN"]} {plugin.Name}");
Logger.WriteDebug(ex.GetExceptionInfo());
}
}
@ -294,7 +304,7 @@ namespace IW4MAdmin.Application
}
newConfig.Servers = newConfig.Servers.Where(_servers => _servers != null).Append((ServerConfiguration)serverConfig.Generate()).ToArray();
} while (Utilities.PromptBool(Utilities.CurrentLocalization.LocalizationIndex["SETUP_SERVER_SAVE"]));
} while (Utilities.PromptBool(_translationLookup["SETUP_SERVER_SAVE"]));
config = newConfig;
await ConfigHandler.Save();
@ -320,9 +330,10 @@ namespace IW4MAdmin.Application
if (!validationResult.IsValid)
{
throw new ConfigurationException(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_CONFIGURATION_ERROR"])
throw new ConfigurationException("MANAGER_CONFIGURATION_ERROR")
{
Errors = validationResult.Errors.Select(_error => _error.ErrorMessage).ToArray()
Errors = validationResult.Errors.Select(_error => _error.ErrorMessage).ToArray(),
ConfigurationFileName = ConfigHandler.FileName
};
}
@ -367,56 +378,39 @@ namespace IW4MAdmin.Application
#endregion
#region COMMANDS
if (ClientSvc.GetOwners().Result.Count == 0)
if (ClientSvc.GetOwners().Result.Count > 0)
{
Commands.Add(new COwner());
_commands.RemoveAll(_cmd => _cmd.GetType() == typeof(OwnerCommand));
}
Commands.Add(new CQuit());
Commands.Add(new CRestart());
Commands.Add(new CKick());
Commands.Add(new CSay());
Commands.Add(new CTempBan());
Commands.Add(new CBan());
Commands.Add(new CWhoAmI());
Commands.Add(new CList());
Commands.Add(new CHelp());
Commands.Add(new CFastRestart());
Commands.Add(new CMapRotate());
Commands.Add(new CSetLevel());
Commands.Add(new CUsage());
Commands.Add(new CUptime());
Commands.Add(new CWarn());
Commands.Add(new CWarnClear());
Commands.Add(new CUnban());
Commands.Add(new CListAdmins());
Commands.Add(new CLoadMap());
Commands.Add(new CFindPlayer());
Commands.Add(new CListRules());
Commands.Add(new CPrivateMessage());
Commands.Add(new CFlag());
Commands.Add(new CUnflag());
Commands.Add(new CReport());
Commands.Add(new CListReports());
Commands.Add(new CListBanInfo());
Commands.Add(new CListAlias());
Commands.Add(new CExecuteRCON());
Commands.Add(new CPlugins());
Commands.Add(new CIP());
Commands.Add(new CMask());
Commands.Add(new CPruneAdmins());
//Commands.Add(new CKillServer());
Commands.Add(new CSetPassword());
Commands.Add(new CPing());
Commands.Add(new CSetGravatar());
Commands.Add(new CNextMap());
Commands.Add(new RequestTokenCommand());
Commands.Add(new UnlinkClientCommand());
List<IManagerCommand> commandsToAddToConfig = new List<IManagerCommand>();
var cmdConfig = _commandConfiguration.Configuration();
foreach (Command C in SharedLibraryCore.Plugins.PluginImporter.ActiveCommands)
if (cmdConfig == null)
{
Commands.Add(C);
cmdConfig = new CommandConfiguration();
commandsToAddToConfig.AddRange(_commands);
}
else
{
var unsavedCommands = _commands.Where(_cmd => !cmdConfig.Commands.Keys.Contains(_cmd.GetType().Name));
commandsToAddToConfig.AddRange(unsavedCommands);
}
foreach (var cmd in commandsToAddToConfig)
{
cmdConfig.Commands.Add(cmd.GetType().Name,
new CommandProperties()
{
Name = cmd.Name,
Alias = cmd.Alias,
MinimumPermission = cmd.Permission
});
}
_commandConfiguration.Set(cmdConfig);
await _commandConfiguration.Save();
#endregion
#region META
@ -557,7 +551,7 @@ namespace IW4MAdmin.Application
try
{
var ServerInstance = new IW4MServer(this, Conf);
var ServerInstance = new IW4MServer(this, Conf, _translationLookup, _pluginImporter);
await ServerInstance.Initialize();
_servers.Add(ServerInstance);
@ -751,7 +745,7 @@ namespace IW4MAdmin.Application
public IList<Assembly> GetPluginAssemblies()
{
return SharedLibraryCore.Plugins.PluginImporter.PluginAssemblies.Union(SharedLibraryCore.Plugins.PluginImporter.Assemblies).ToList();
return _pluginImporter.PluginAssemblies.Union(_pluginImporter.Assemblies).ToList();
}
public IPageList GetPageList()

View File

@ -23,14 +23,18 @@ namespace IW4MAdmin
{
public class IW4MServer : Server
{
private static readonly SharedLibraryCore.Localization.Index loc = Utilities.CurrentLocalization.LocalizationIndex;
private static readonly SharedLibraryCore.Localization.TranslationLookup loc = Utilities.CurrentLocalization.LocalizationIndex;
private GameLogEventDetection LogEvent;
private readonly ITranslationLookup _translationLookup;
private const int REPORT_FLAG_COUNT = 4;
private readonly IPluginImporter _pluginImporter;
public int Id { get; private set; }
public IW4MServer(IManager mgr, ServerConfiguration cfg) : base(mgr, cfg)
public IW4MServer(IManager mgr, ServerConfiguration cfg, ITranslationLookup lookup, IPluginImporter pluginImporter) : base(mgr, cfg)
{
_translationLookup = lookup;
_pluginImporter = pluginImporter;
}
override public async Task<EFClient> OnClientConnected(EFClient clientFromLog)
@ -148,7 +152,7 @@ namespace IW4MAdmin
}
}
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
foreach (var plugin in _pluginImporter.ActivePlugins)
{
try
{
@ -666,7 +670,7 @@ namespace IW4MAdmin
await e.WaitAsync(Utilities.DefaultCommandTimeout, new CancellationTokenRegistration().Token);
}
foreach (var plugin in SharedLibraryCore.Plugins.PluginImporter.ActivePlugins)
foreach (var plugin in _pluginImporter.ActivePlugins)
{
await plugin.OnUnloadAsync();
}
@ -1186,8 +1190,8 @@ namespace IW4MAdmin
{
Manager.GetMessageTokens().Add(new MessageToken("TOTALPLAYERS", (Server s) => Task.Run(async () => (await Manager.GetClientService().GetTotalClientsAsync()).ToString())));
Manager.GetMessageTokens().Add(new MessageToken("VERSION", (Server s) => Task.FromResult(Application.Program.Version.ToString())));
Manager.GetMessageTokens().Add(new MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.CNextMap.GetNextMap(s)));
Manager.GetMessageTokens().Add(new MessageToken("ADMINS", (Server s) => Task.FromResult(SharedLibraryCore.Commands.CListAdmins.OnlineAdmins(s))));
Manager.GetMessageTokens().Add(new MessageToken("NEXTMAP", (Server s) => SharedLibraryCore.Commands.NextMapCommand.GetNextMap(s, _translationLookup)));
Manager.GetMessageTokens().Add(new MessageToken("ADMINS", (Server s) => Task.FromResult(SharedLibraryCore.Commands.ListAdminsCommand.OnlineAdmins(s, _translationLookup))));
}
}
}

View File

@ -1,30 +1,29 @@
using IW4MAdmin.Application.API.Master;
using SharedLibraryCore;
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace IW4MAdmin.Application.Localization
{
public class Configure
{
public static void Initialize(string customLocale = null)
public static ITranslationLookup Initialize(bool useLocalTranslation, string customLocale = null)
{
string currentLocale = string.IsNullOrEmpty(customLocale) ? CultureInfo.CurrentCulture.Name : customLocale;
string[] localizationFiles = Directory.GetFiles(Path.Join(Utilities.OperatingDirectory, "Localization"), $"*.{currentLocale}.json");
if (!Program.ServerManager.GetApplicationSettings()?.Configuration()?.UseLocalTranslations ?? false)
if (!useLocalTranslation)
{
try
{
var api = Endpoint.Get();
var localization = api.GetLocalization(currentLocale).Result;
Utilities.CurrentLocalization = localization;
return;
return localization.LocalizationIndex;
}
catch (Exception)
@ -73,6 +72,8 @@ namespace IW4MAdmin.Application.Localization
{
LocalizationName = currentLocale,
};
return Utilities.CurrentLocalization.LocalizationIndex;
}
}
}

View File

@ -1,11 +1,14 @@
using IW4MAdmin.Application.Migration;
using IW4MAdmin.Application.Helpers;
using IW4MAdmin.Application.Migration;
using IW4MAdmin.Application.Misc;
using Microsoft.Extensions.DependencyInjection;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Exceptions;
using SharedLibraryCore.Helpers;
using SharedLibraryCore.Interfaces;
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
@ -18,6 +21,7 @@ namespace IW4MAdmin.Application
public static ApplicationManager ServerManager;
private static Task ApplicationTask;
private static readonly BuildNumber _fallbackVersion = BuildNumber.Parse("99.99.99.99");
private static ServiceProvider serviceProvider;
/// <summary>
/// entrypoint of the application
@ -60,17 +64,34 @@ namespace IW4MAdmin.Application
private static async Task LaunchAsync()
{
restart:
ITranslationLookup translationLookup = null;
try
{
var services = ConfigureServices();
using (var builder = services.BuildServiceProvider())
{
ServerManager = (ApplicationManager)builder.GetRequiredService<IManager>();
translationLookup = builder.GetRequiredService<ITranslationLookup>();
var importer = builder.GetRequiredService<IPluginImporter>();
importer.Load();
foreach (var type in importer.CommandTypes)
{
services.AddTransient(typeof(IManagerCommand), type);
}
foreach (var commandDefinition in typeof(SharedLibraryCore.Commands.QuitCommand).Assembly.GetTypes()
.Where(_command => _command.BaseType == typeof(Command)))
{
services.AddTransient(typeof(IManagerCommand), commandDefinition);
}
}
var configuration = ServerManager.GetApplicationSettings().Configuration();
Localization.Configure.Initialize(configuration?.EnableCustomLocale ?? false ? (configuration.CustomLocale ?? "en-US") : "en-US");
serviceProvider = services.BuildServiceProvider();
var pluginImporter = serviceProvider.GetRequiredService<IPluginImporter>();
pluginImporter.Load();
ServerManager = (ApplicationManager)serviceProvider.GetRequiredService<IManager>();
// do any needed housekeeping file/folder migrations
ConfigurationMigration.MoveConfigFolder10518(null);
@ -78,16 +99,14 @@ namespace IW4MAdmin.Application
ServerManager.Logger.WriteInfo(Utilities.CurrentLocalization.LocalizationIndex["MANAGER_VERSION"].FormatExt(Version));
await CheckVersion();
await CheckVersion(translationLookup);
await ServerManager.Init();
}
catch (Exception e)
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
string failMessage = loc == null ? "Failed to initalize IW4MAdmin" : loc["MANAGER_INIT_FAIL"];
string exitMessage = loc == null ? "Press any key to exit..." : loc["MANAGER_EXIT"];
string failMessage = translationLookup == null ? "Failed to initalize IW4MAdmin" : translationLookup["MANAGER_INIT_FAIL"];
string exitMessage = translationLookup == null ? "Press any key to exit..." : translationLookup["MANAGER_EXIT"];
Console.WriteLine(failMessage);
@ -96,16 +115,21 @@ namespace IW4MAdmin.Application
e = e.InnerException;
}
Console.WriteLine(e.Message);
if (e is ConfigurationException cfgE)
if (e is ConfigurationException configException)
{
foreach (string error in cfgE.Errors)
Console.WriteLine(translationLookup[configException.Message].FormatExt(configException.ConfigurationFileName));
foreach (string error in configException.Errors)
{
Console.WriteLine(error);
}
}
else
{
Console.WriteLine(e.Message);
}
Console.WriteLine(exitMessage);
Console.ReadKey();
return;
@ -123,6 +147,8 @@ namespace IW4MAdmin.Application
{
goto restart;
}
serviceProvider.Dispose();
}
/// <summary>
@ -132,7 +158,7 @@ namespace IW4MAdmin.Application
private static async Task RunApplicationTasksAsync()
{
var webfrontTask = ServerManager.GetApplicationSettings().Configuration().EnableWebFront ?
WebfrontCore.Program.Init(ServerManager, ServerManager.CancellationToken) :
WebfrontCore.Program.Init(ServerManager, serviceProvider, ServerManager.CancellationToken) :
Task.CompletedTask;
// we want to run this one on a manual thread instead of letting the thread pool handle it,
@ -156,10 +182,10 @@ namespace IW4MAdmin.Application
/// notifies user if an update is available
/// </summary>
/// <returns></returns>
private static async Task CheckVersion()
private static async Task CheckVersion(ITranslationLookup translationLookup)
{
var api = API.Master.Endpoint.Get();
var loc = Utilities.CurrentLocalization.LocalizationIndex;
var loc = translationLookup;
var version = new API.Master.VersionInfo()
{
@ -259,8 +285,19 @@ namespace IW4MAdmin.Application
{
var serviceProvider = new ServiceCollection();
serviceProvider.AddSingleton<IManager, ApplicationManager>()
.AddSingleton(new BaseConfigurationHandler<ApplicationConfiguration>("IW4MAdminSettings") as IConfigurationHandler<ApplicationConfiguration>)
.AddSingleton(new BaseConfigurationHandler<CommandConfiguration>("CommandConfiguration") as IConfigurationHandler<CommandConfiguration>)
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration())
.AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService<IConfigurationHandler<CommandConfiguration>>().Configuration())
.AddSingleton<ILogger>(_serviceProvider => new Logger("IW4MAdmin-Manager"))
.AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>();
.AddSingleton<IPluginImporter, PluginImporter>()
.AddSingleton<IMiddlewareActionHandler, MiddlewareActionHandler>()
.AddSingleton(_serviceProvider =>
{
var config = _serviceProvider.GetRequiredService<IConfigurationHandler<ApplicationConfiguration>>().Configuration();
return Localization.Configure.Initialize(useLocalTranslation: config?.UseLocalTranslations ?? false,
customLocale: config?.EnableCustomLocale ?? false ? (config.CustomLocale ?? "en-US") : "en-US");
});
return serviceProvider;
}

View File

@ -4,17 +4,30 @@ using System.Collections.Generic;
using System.Reflection;
using SharedLibraryCore.Interfaces;
using System.Linq;
using SharedLibraryCore;
namespace SharedLibraryCore.Plugins
namespace IW4MAdmin.Application.Helpers
{
public class PluginImporter
public class PluginImporter : IPluginImporter
{
public static List<Command> ActiveCommands = new List<Command>();
public static List<IPlugin> ActivePlugins = new List<IPlugin>();
public static List<Assembly> PluginAssemblies = new List<Assembly>();
public static List<Assembly> Assemblies = new List<Assembly>();
public IList<Type> CommandTypes { get; private set; } = new List<Type>();
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>();
public static bool Load(IManager Manager)
private readonly ILogger _logger;
private readonly ITranslationLookup _translationLookup;
public PluginImporter(ILogger logger, ITranslationLookup translationLookup)
{
_logger = logger;
_translationLookup = translationLookup;
}
/// <summary>
/// Loads all the assembly and javascript plugins
/// </summary>
public void Load()
{
string pluginDir = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}";
string[] dllFileNames = null;
@ -35,16 +48,15 @@ namespace SharedLibraryCore.Plugins
if (dllFileNames.Length == 0 &&
scriptFileNames.Length == 0)
{
Manager.GetLogger(0).WriteDebug(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_NOTFOUND"]);
return true;
_logger.WriteDebug(_translationLookup["PLUGIN_IMPORTER_NOTFOUND"]);
return;
}
// load up the script plugins
foreach (string fileName in scriptFileNames)
{
var plugin = new ScriptPlugin(fileName);
plugin.Initialize(Manager).Wait();
Manager.GetLogger(0).WriteDebug($"Loaded script plugin \"{ plugin.Name }\" [{plugin.Version}]");
_logger.WriteDebug($"Loaded script plugin \"{ plugin.Name }\" [{plugin.Version}]");
ActivePlugins.Add(plugin);
}
@ -63,12 +75,10 @@ namespace SharedLibraryCore.Plugins
Type[] types = Plugin.GetTypes();
foreach (Type assemblyType in types)
{
if (assemblyType.IsClass && assemblyType.BaseType.Name == "Command")
if (assemblyType.IsClass && assemblyType.BaseType == typeof(Command))
{
Object commandObject = Activator.CreateInstance(assemblyType);
Command newCommand = (Command)commandObject;
ActiveCommands.Add(newCommand);
Manager.GetLogger(0).WriteDebug($"{Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_REGISTERCMD"]} \"{newCommand.Name}\"");
CommandTypes.Add(assemblyType);
_logger.WriteDebug($"{_translationLookup["PLUGIN_IMPORTER_REGISTERCMD"]} \"{assemblyType.Name}\"");
LoadedCommands++;
continue;
}
@ -78,26 +88,26 @@ namespace SharedLibraryCore.Plugins
if (assemblyType.GetInterface("IPlugin", false) == null)
continue;
Object notifyObject = Activator.CreateInstance(assemblyType);
var notifyObject = Activator.CreateInstance(assemblyType);
IPlugin newNotify = (IPlugin)notifyObject;
if (ActivePlugins.Find(x => x.Name == newNotify.Name) == null)
if (ActivePlugins.FirstOrDefault(x => x.Name == newNotify.Name) == null)
{
ActivePlugins.Add(newNotify);
PluginAssemblies.Add(Plugin);
Manager.GetLogger(0).WriteDebug($"Loaded plugin \"{ newNotify.Name }\" [{newNotify.Version}]");
_logger.WriteDebug($"Loaded plugin \"{newNotify.Name}\" [{newNotify.Version}]");
}
}
catch (Exception e)
{
Manager.GetLogger(0).WriteWarning(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(Plugin.Location));
Manager.GetLogger(0).WriteDebug(e.GetExceptionInfo());
_logger.WriteWarning(_translationLookup["PLUGIN_IMPORTER_ERROR"].FormatExt(Plugin.Location));
_logger.WriteDebug(e.GetExceptionInfo());
}
}
}
}
Manager.GetLogger(0).WriteInfo($"Loaded {ActivePlugins.Count} plugins and registered {LoadedCommands} commands.");
return true;
_logger.WriteInfo($"Loaded {ActivePlugins.Count} plugins and registered {LoadedCommands} plugin commands.");
}
}
}

View File

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

View File

@ -1,5 +1,8 @@
using SharedLibraryCore;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Threading.Tasks;
namespace IW4ScriptCommands.Commands
@ -9,15 +12,22 @@ namespace IW4ScriptCommands.Commands
/// </summary>
public class KillPlayerCommand : Command
{
public KillPlayerCommand() : base("killplayer", "kill a player", "kp", EFClient.Permission.Administrator, true, new[]
public KillPlayerCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
new CommandArgument()
Name = "killplayer";
Description = "kill a player";
Alias = "kp";
Permission = EFClient.Permission.Administrator;
RequiresTarget = true;
Arguments = new[]
{
Name = "player",
Required = true
}
})
{ }
new CommandArgument()
{
Name = "player",
Required = true
}
};
}
public override async Task ExecuteAsync(GameEvent E)
{

View File

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

View File

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

View File

@ -1,39 +0,0 @@
using SharedLibraryCore;
using SharedLibraryCore.Database.Models;
using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Login.Commands
{
public class CLogin : Command
{
public CLogin() : base("login", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"], "li", EFClient.Permission.Trusted, false, new CommandArgument[]
{
new CommandArgument()
{
Name = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_PASSWORD"],
Required = true
}
})
{ }
public override async Task ExecuteAsync(GameEvent E)
{
bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
if (!success)
{
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, E.Origin.PasswordSalt));
success = hashedPassword[0] == E.Origin.Password;
}
if (success)
{
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
}
_ = success ?
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]) :
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
}
}
}

View File

@ -0,0 +1,49 @@
using SharedLibraryCore;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Threading.Tasks;
namespace IW4MAdmin.Plugins.Login.Commands
{
public class LoginCommand : Command
{
public LoginCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "login";
Description = _translationLookup["PLUGINS_LOGIN_COMMANDS_LOGIN_DESC"];
Alias = "li";
Permission = EFClient.Permission.Trusted;
RequiresTarget = false;
Arguments = new CommandArgument[]
{
new CommandArgument()
{
Name = Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_ARGS_PASSWORD"],
Required = true
}
};
}
public override async Task ExecuteAsync(GameEvent E)
{
bool success = E.Owner.Manager.TokenAuthenticator.AuthorizeToken(E.Origin.NetworkId, E.Data);
if (!success)
{
string[] hashedPassword = await Task.FromResult(SharedLibraryCore.Helpers.Hashing.Hash(E.Data, E.Origin.PasswordSalt));
success = hashedPassword[0] == E.Origin.Password;
}
if (success)
{
Plugin.AuthorizedClients[E.Origin.ClientId] = true;
}
_ = success ?
E.Origin.Tell(_translationLookup["PLUGINS_LOGIN_COMMANDS_LOGIN_SUCCESS"]) :
E.Origin.Tell(_translationLookup["PLUGINS_LOGIN_COMMANDS_LOGIN_FAIL"]);
}
}
}

View File

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

View File

@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Reflection;
using System.Threading.Tasks;
using IW4MAdmin.Plugins.Login.Commands;
using SharedLibraryCore;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
@ -43,11 +44,11 @@ namespace IW4MAdmin.Plugins.Login
E.Origin.Level == EFClient.Permission.Console)
return Task.CompletedTask;
if (((Command)E.Extra).Name == new CSetPassword().Name &&
if (E.Extra.GetType() == typeof(SetPasswordCommand) &&
E.Origin?.Password == null)
return Task.CompletedTask;
if (((Command)E.Extra).Name == new Commands.CLogin().Name)
if (E.Extra.GetType() == typeof(LoginCommand))
return Task.CompletedTask;
if (E.Extra.GetType() == typeof(RequestTokenCommand))

View File

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

View File

@ -9,12 +9,14 @@ using SharedLibraryCore.Database;
using System.Collections.Generic;
using SharedLibraryCore.Database.Models;
using IW4MAdmin.Plugins.Stats.Helpers;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Plugins.Stats.Commands
{
class MostPlayed : Command
class MostPlayedCommand : Command
{
public static async Task<List<string>> GetMostPlayed(Server s)
public static async Task<List<string>> GetMostPlayed(Server s, ITranslationLookup translationLookup)
{
long serverId = StatManager.GetIdForServer(s);
@ -50,18 +52,25 @@ namespace IW4MAdmin.Plugins.Stats.Commands
var iqList = await iqStats.ToListAsync();
mostPlayed.AddRange(iqList.Select(stats =>
$"^3{stats.Name}^7 - ^5{stats.Kills} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KILLS"]} | ^5{Utilities.GetTimePassed(DateTime.UtcNow.AddSeconds(-stats.TotalConnectionTime), false)} ^7{Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_PROFILE_PLAYER"].ToLower()}"));
$"^3{stats.Name}^7 - ^5{stats.Kills} ^7{translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{Utilities.GetTimePassed(DateTime.UtcNow.AddSeconds(-stats.TotalConnectionTime), false)} ^7{translationLookup["WEBFRONT_PROFILE_PLAYER"].ToLower()}"));
}
return mostPlayed;
}
public MostPlayed() : base("mostplayed", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"], "mp", EFClient.Permission.User, false) { }
public MostPlayedCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "mostplayed";
Description = translationLookup["PLUGINS_STATS_COMMANDS_MOSTPLAYED_DESC"];
Alias = "mp";
Permission = EFClient.Permission.User;
RequiresTarget = false;
}
public override async Task ExecuteAsync(GameEvent E)
{
var topStats = await GetMostPlayed(E.Owner);
var topStats = await GetMostPlayed(E.Owner, _translationLookup);
if (!E.Message.IsBroadcastCommand())
{
foreach (var stat in topStats)

View File

@ -1,8 +1,10 @@
using IW4MAdmin.Plugins.Stats.Models;
using Microsoft.EntityFrameworkCore;
using SharedLibraryCore;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Linq;
using System.Threading.Tasks;
@ -10,7 +12,14 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
public class ResetStats : Command
{
public ResetStats() : base("resetstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_DESC"], "rs", EFClient.Permission.User, false) { }
public ResetStats(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "resetstats";
Description = translationLookup["PLUGINS_STATS_COMMANDS_RESET_DESC"];
Alias = "rs";
Permission = EFClient.Permission.User;
RequiresTarget = false;
}
public override async Task ExecuteAsync(GameEvent E)
{
@ -41,12 +50,12 @@ namespace IW4MAdmin.Plugins.Stats.Commands
// fixme: this doesn't work properly when another context exists
await ctx.SaveChangesAsync();
}
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]);
E.Origin.Tell(_translationLookup["PLUGINS_STATS_COMMANDS_RESET_SUCCESS"]);
}
else
{
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_RESET_FAIL"]);
E.Origin.Tell(_translationLookup["PLUGINS_STATS_COMMANDS_RESET_FAIL"]);
}
}
}

View File

@ -9,17 +9,19 @@ using SharedLibraryCore.Database;
using System.Collections.Generic;
using SharedLibraryCore.Database.Models;
using IW4MAdmin.Plugins.Stats.Helpers;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
namespace IW4MAdmin.Plugins.Stats.Commands
{
class TopStats : Command
{
public static async Task<List<string>> GetTopStats(Server s)
public static async Task<List<string>> GetTopStats(Server s, ITranslationLookup translationLookup)
{
long serverId = StatManager.GetIdForServer(s);
List<string> topStatsText = new List<string>()
var topStatsText = new List<string>()
{
$"^5--{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--"
$"^5--{translationLookup["PLUGINS_STATS_COMMANDS_TOP_TEXT"]}--"
};
using (var db = new DatabaseContext(true))
@ -46,7 +48,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
.Take(5);
var statsList = (await iqStats.ToListAsync())
.Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}");
.Select(stats => $"^3{stats.Name}^7 - ^5{stats.KDR} ^7{translationLookup["PLUGINS_STATS_TEXT_KDR"]} | ^5{stats.Performance} ^7{translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"]}");
topStatsText.AddRange(statsList);
}
@ -56,18 +58,25 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
topStatsText = new List<string>()
{
Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_TEXT_NOQUALIFY"]
translationLookup["PLUGINS_STATS_TEXT_NOQUALIFY"]
};
}
return topStatsText;
}
public TopStats() : base("topstats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_TOP_DESC"], "ts", EFClient.Permission.User, false) { }
public TopStats(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "topstats";
Description = translationLookup["PLUGINS_STATS_COMMANDS_TOP_DESC"];
Alias = "ts";
Permission = EFClient.Permission.User;
RequiresTarget = false;
}
public override async Task ExecuteAsync(GameEvent E)
{
var topStats = await GetTopStats(E.Owner);
var topStats = await GetTopStats(E.Owner, _translationLookup);
if (!E.Message.IsBroadcastCommand())
{
foreach (var stat in topStats)

View File

@ -1,35 +1,40 @@
using SharedLibraryCore;
using SharedLibraryCore.Services;
using IW4MAdmin.Plugins.Stats.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharedLibraryCore.Database;
using Microsoft.EntityFrameworkCore;
using IW4MAdmin.Plugins.Stats.Helpers;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Interfaces;
using SharedLibraryCore.Commands;
namespace IW4MAdmin.Plugins.Stats.Commands
{
public class CViewStats : Command
public class ViewStatsCommand : Command
{
public CViewStats() : base("stats", Utilities.CurrentLocalization.LocalizationIndex["PLUGINS_STATS_COMMANDS_VIEW_DESC"], "xlrstats", EFClient.Permission.User, false, new CommandArgument[]
public ViewStatsCommand(CommandConfiguration config, ITranslationLookup translationLookup) : base(config, translationLookup)
{
Name = "stats";
Description = translationLookup["PLUGINS_STATS_COMMANDS_VIEW_DESC"];
Alias = "xlrstats";
Permission = EFClient.Permission.User;
RequiresTarget = false;
Arguments = new CommandArgument[]
{
new CommandArgument()
{
Name = "player",
Required = false
}
})
{ }
};
}
public override async Task ExecuteAsync(GameEvent E)
{
var loc = Utilities.CurrentLocalization.LocalizationIndex;
String statLine;
string statLine;
EFClientStatistics pStats;
if (E.Data.Length > 0 && E.Target == null)
@ -38,7 +43,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
if (E.Target == null)
{
E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]);
E.Origin.Tell(_translationLookup["PLUGINS_STATS_COMMANDS_VIEW_FAIL"]);
}
}
@ -48,7 +53,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
if (E.Target != null)
{
int performanceRanking = await StatManager.GetClientOverallRanking(E.Target.ClientId);
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
string performanceRankingString = performanceRanking == 0 ? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
if (E.Owner.GetClientsAsList().Any(_client => _client.Equals(E.Target)))
{
@ -62,13 +67,13 @@ namespace IW4MAdmin.Plugins.Stats.Commands
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Target.ClientId));
}
}
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
statLine = $"^5{pStats.Kills} ^7{_translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{_translationLookup["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{_translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
}
else
{
int performanceRanking = await StatManager.GetClientOverallRanking(E.Origin.ClientId);
string performanceRankingString = performanceRanking == 0 ? loc["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{loc["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
string performanceRankingString = performanceRanking == 0 ? _translationLookup["WEBFRONT_STATS_INDEX_UNRANKED"] : $"{_translationLookup["WEBFRONT_STATS_INDEX_RANKED"]} #{performanceRanking}";
if (E.Owner.GetClientsAsList().Any(_client => _client.Equals(E.Origin)))
{
@ -82,13 +87,13 @@ namespace IW4MAdmin.Plugins.Stats.Commands
pStats = (await ctx.Set<EFClientStatistics>().FirstAsync(c => c.ServerId == serverId && c.ClientId == E.Origin.ClientId));
}
}
statLine = $"^5{pStats.Kills} ^7{loc["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{loc["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{loc["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
statLine = $"^5{pStats.Kills} ^7{_translationLookup["PLUGINS_STATS_TEXT_KILLS"]} | ^5{pStats.Deaths} ^7{_translationLookup["PLUGINS_STATS_TEXT_DEATHS"]} | ^5{pStats.KDR} ^7KDR | ^5{pStats.Performance} ^7{_translationLookup["PLUGINS_STATS_COMMANDS_PERFORMANCE"].ToUpper()} | {performanceRankingString}";
}
if (E.Message.IsBroadcastCommand())
{
string name = E.Target == null ? E.Origin.Name : E.Target.Name;
E.Owner.Broadcast(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(name));
E.Owner.Broadcast(_translationLookup["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(name));
E.Owner.Broadcast(statLine);
}
@ -96,7 +101,7 @@ namespace IW4MAdmin.Plugins.Stats.Commands
{
if (E.Target != null)
{
E.Origin.Tell(loc["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(E.Target.Name));
E.Origin.Tell(_translationLookup["PLUGINS_STATS_COMMANDS_VIEW_SUCCESS"].FormatExt(E.Target.Name));
}
E.Origin.Tell(statLine);

View File

@ -477,12 +477,14 @@ namespace IW4MAdmin.Plugins.Stats
async Task<string> topStats(Server s)
{
return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s));
// todo: this needs to needs to be updated when we DI the lookup
return string.Join(Environment.NewLine, await Commands.TopStats.GetTopStats(s, Utilities.CurrentLocalization.LocalizationIndex));
}
async Task<string> mostPlayed(Server s)
{
return string.Join(Environment.NewLine, await Commands.MostPlayed.GetMostPlayed(s));
// todo: this needs to needs to be updated when we DI the lookup
return string.Join(Environment.NewLine, await Commands.MostPlayedCommand.GetMostPlayed(s, Utilities.CurrentLocalization.LocalizationIndex));
}
manager.GetMessageTokens().Add(new MessageToken("TOTALKILLS", totalKills));

View File

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

View File

@ -293,7 +293,7 @@ namespace Tests
var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault();
Assert.False(client == null, "no client found to tempban");
var tbCommand = new CTempBan();
/* var tbCommand = new TempBanCommand();
tbCommand.ExecuteAsync(new GameEvent()
{
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
@ -304,7 +304,7 @@ namespace Tests
}).Wait();
Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.TempBan) == 1,
"tempban was not added");
"tempban was not added");*/
}
[Fact]
@ -317,8 +317,8 @@ namespace Tests
var client = _manager.Servers.First().GetClientsAsList().FirstOrDefault();
Assert.False(client == null, "no client found to ban");
var banCommand = new CBan();
/*
var banCommand = new BanCommand();
banCommand.ExecuteAsync(new GameEvent()
{
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
@ -331,7 +331,7 @@ namespace Tests
Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 1,
"ban was not added");
var unbanCommand = new CUnban();
var unbanCommand = new UnbanCommand();
unbanCommand.ExecuteAsync(new GameEvent()
{
Origin = new EFClient() { ClientId = 1, Level = EFClient.Permission.Console, CurrentServer = client.CurrentServer },
@ -342,7 +342,7 @@ namespace Tests
}).Wait();
Assert.True(_manager.GetPenaltyService().GetActivePenaltiesAsync(client.AliasLinkId).Result.Count(p => p.Type == EFPenalty.PenaltyType.Ban) == 0,
"ban was not removed");
"ban was not removed");*/
}
}

View File

@ -53,7 +53,7 @@ namespace Tests
foreach (var command in Manager.GetCommands().OrderByDescending(c => c.Permission).ThenBy(c => c.Name))
{
sb.AppendLine($"|{command.Name}|{command.Alias}|{command.Description}|{command.RequiresTarget}|{command.Syntax.Substring(8).EscapeMarkdown()}|{command.Permission}|");
sb.AppendLine($"|{command.Name}|{command.Alias}|{command.Description}|{((Command)command).RequiresTarget}|{((Command)command).Syntax.Substring(8).EscapeMarkdown()}|{command.Permission}|");
}
Assert.True(false, sb.ToString());

View File

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

View File

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

View File

@ -25,7 +25,7 @@ namespace SharedLibraryCore
public IManager Manager { get; private set; }
protected readonly DatabaseContext Context;
protected bool Authorized { get; set; }
protected SharedLibraryCore.Localization.Index Localization { get; private set; }
protected SharedLibraryCore.Localization.TranslationLookup Localization { get; private set; }
protected EFClient Client { get; private set; }
private static readonly byte[] LocalHost = { 127, 0, 0, 1 };
private static string SocialLink;

View File

@ -1,38 +1,121 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SharedLibraryCore.Commands;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
namespace SharedLibraryCore
{
public class CommandArgument
/// <summary>
/// Abstract class for command
/// </summary>
public abstract class Command : IManagerCommand
{
public string Name { get; set; }
public bool Required { get; set; }
}
private readonly CommandConfiguration _config;
protected readonly ITranslationLookup _translationLookup;
public abstract class Command
{
public Command(String commandName, String commandDescription, String commandAlias, EFClient.Permission requiredPermission, bool requiresTarget, CommandArgument[] param = null)
{
Name = commandName;
Description = commandDescription;
Alias = commandAlias;
Permission = requiredPermission;
RequiresTarget = requiresTarget;
Arguments = param ?? new CommandArgument[0];
public Command(CommandConfiguration config, ITranslationLookup layout)
{
_config = config;
_translationLookup = layout;
}
//Execute the command
/// <summary>
/// Executes the command
/// </summary>
/// <param name="E"></param>
/// <returns></returns>
abstract public Task ExecuteAsync(GameEvent E);
public String Name { get; private set; }
public String Description { get; private set; }
public String Syntax => $"{Utilities.CurrentLocalization.LocalizationIndex["COMMAND_HELP_SYNTAX"]} !{Alias} {String.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : Utilities.CurrentLocalization.LocalizationIndex["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}";
public String Alias { get; private set; }
/// <summary>
/// Specifies the name and string that triggers the command
/// </summary>
public string Name
{
get => name;
protected set
{
try
{
name = _config?.Commands[GetType().Name].Name ?? value;
}
catch (KeyNotFoundException)
{
name = value;
}
}
}
private string name;
/// <summary>
/// Specifies the command description
/// </summary>
public string Description { get; protected set; }
/// <summary>
/// Helper property to provide the syntax of the command
/// </summary>
public string Syntax => $"{_translationLookup["COMMAND_HELP_SYNTAX"]} !{Alias} {string.Join(" ", Arguments.Select(a => $"<{(a.Required ? "" : _translationLookup["COMMAND_HELP_OPTIONAL"] + " ")}{a.Name}>"))}";
/// <summary>
/// Alternate name for this command to be executed by
/// </summary>
public string Alias
{
get => alias;
protected set
{
try
{
alias = _config?.Commands[GetType().Name].Alias ?? value;
}
catch (KeyNotFoundException)
{
alias = value;
}
}
}
private string alias;
/// <summary>
/// Helper property to determine the number of required args
/// </summary>
public int RequiredArgumentCount => Arguments.Count(c => c.Required);
public bool RequiresTarget { get; private set; }
public EFClient.Permission Permission { get; private set; }
public CommandArgument[] Arguments { get; private set; }
/// <summary>
/// Indicates if the command requires a target to execute on
/// </summary>
public bool RequiresTarget { get; protected set; }
/// <summary>
/// Minimum permission level to execute command
/// </summary>
public EFClient.Permission Permission
{
get => permission;
protected set
{
try
{
permission = _config?.Commands[GetType().Name].MinimumPermission ?? value;
}
catch (KeyNotFoundException)
{
permission = value;
}
}
}
private EFClient.Permission permission;
/// <summary>
/// Argument list for the command
/// </summary>
public CommandArgument[] Arguments { get; protected set; } = new CommandArgument[0];
}
}

View File

@ -0,0 +1,18 @@
namespace SharedLibraryCore.Commands
{
/// <summary>
/// Holds information about command args
/// </summary>
public class CommandArgument
{
/// <summary>
/// Name of the argument
/// </summary>
public string Name { get; set; }
/// <summary>
/// Indicates if the argument is required
/// </summary>
public bool Required { get; set; }
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,28 @@
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Threading.Tasks;
namespace SharedLibraryCore.Commands
{
/// <summary>
/// Generates a token for use in webfront login
/// </summary>
public class RequestTokenCommand : Command
{
public RequestTokenCommand() :
base("requesttoken", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_DESC"], "rt", EFClient.Permission.Trusted, false)
{ }
public RequestTokenCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
Name = "requesttoken";
Description = lookup["COMMANDS_GENERATETOKEN_DESC"];
Alias = "rt";
Permission = EFClient.Permission.Trusted;
RequiresTarget = false;
}
public override Task ExecuteAsync(GameEvent E)
{
var state = E.Owner.Manager.TokenAuthenticator.GenerateNextToken(E.Origin.NetworkId);
E.Origin.Tell(string.Format(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, $"{state.RemainingTime} {Utilities.CurrentLocalization.LocalizationIndex["GLOBAL_MINUTES"]}", E.Origin.ClientId));
E.Origin.Tell(string.Format(_translationLookup["COMMANDS_GENERATETOKEN_SUCCESS"], state.Token, $"{state.RemainingTime} {_translationLookup["GLOBAL_MINUTES"]}", E.Origin.ClientId));
return Task.CompletedTask;
}

View File

@ -1,4 +1,6 @@
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Configuration;
using SharedLibraryCore.Database.Models;
using SharedLibraryCore.Interfaces;
using System.Threading.Tasks;
namespace SharedLibraryCore.Commands
@ -12,14 +14,19 @@ namespace SharedLibraryCore.Commands
/// </summary>
public class UnlinkClientCommand : Command
{
public UnlinkClientCommand() :
base("unlinkclient", Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNLINK_CLIENT_DESC"], "uc", EFClient.Permission.Administrator, true)
{ }
public UnlinkClientCommand(CommandConfiguration config, ITranslationLookup lookup) : base(config, lookup)
{
Name = "unlinkclient";
Description = lookup["COMMANDS_UNLINK_CLIENT_DESC"];
Alias = "uc";
Permission = EFClient.Permission.Administrator;
RequiresTarget = true;
}
public override async Task ExecuteAsync(GameEvent E)
{
await E.Owner.Manager.GetClientService().UnlinkClient(E.Target.ClientId);
E.Origin.Tell(Utilities.CurrentLocalization.LocalizationIndex["COMMANDS_UNLINK_CLIENT_SUCCESS"].FormatExt(E.Target));
E.Origin.Tell(_translationLookup["COMMANDS_UNLINK_CLIENT_SUCCESS"].FormatExt(E.Target));
}
}
}

View File

@ -0,0 +1,24 @@
using SharedLibraryCore.Interfaces;
using System;
using System.Collections.Generic;
namespace SharedLibraryCore.Configuration
{
/// <summary>
/// Basic command configuration
/// </summary>
public class CommandConfiguration : IBaseConfiguration
{
/// <summary>
/// Dict of command class names mapped to configurable properties
/// </summary>
public Dictionary<string, CommandProperties> Commands { get; set; } = new Dictionary<string, CommandProperties>();
public IBaseConfiguration Generate()
{
throw new NotImplementedException();
}
public string Name() => nameof(CommandConfiguration);
}
}

View File

@ -0,0 +1,28 @@
using Newtonsoft.Json.Converters;
using System.Text.Json.Serialization;
using static SharedLibraryCore.Database.Models.EFClient;
namespace SharedLibraryCore.Configuration
{
/// <summary>
/// Config driven command properties
/// </summary>
public class CommandProperties
{
/// <summary>
/// Specifies the command name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Alias of this command
/// </summary>
public string Alias { get; set; }
/// <summary>
/// Specifies the minimum permission level needed to execute the
/// </summary>
[JsonConverter(typeof(StringEnumConverter))]
public Permission MinimumPermission { get; set; }
}
}

View File

@ -5,6 +5,7 @@ namespace SharedLibraryCore.Exceptions
public class ConfigurationException : Exception
{
public string[] Errors { get; set; }
public string ConfigurationFileName { get; set; }
public ConfigurationException(string message) : base(message) { }
}

View File

@ -9,20 +9,21 @@ namespace SharedLibraryCore.Configuration
{
public class BaseConfigurationHandler<T> : IConfigurationHandler<T> where T : IBaseConfiguration
{
readonly string _configurationPath;
T _configuration;
public BaseConfigurationHandler(string fn)
{
_configurationPath = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fn}.json");
FileName = Path.Join(Utilities.OperatingDirectory, "Configuration", $"{fn}.json");
Build();
}
public string FileName { get; }
public void Build()
{
try
{
var configContent = File.ReadAllText(_configurationPath);
var configContent = File.ReadAllText(FileName);
_configuration = JsonConvert.DeserializeObject<T>(configContent);
}
@ -35,15 +36,22 @@ namespace SharedLibraryCore.Configuration
{
throw new ConfigurationException("MANAGER_CONFIGURATION_ERROR")
{
Errors = new[] { e.Message }
Errors = new[] { e.Message },
ConfigurationFileName = FileName
};
}
}
public Task Save()
{
var appConfigJSON = JsonConvert.SerializeObject(_configuration, Formatting.Indented);
return File.WriteAllTextAsync(_configurationPath, appConfigJSON);
var settings = new JsonSerializerSettings()
{
Formatting = Formatting.Indented
};
settings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
var appConfigJSON = JsonConvert.SerializeObject(_configuration, settings);
return File.WriteAllTextAsync(FileName, appConfigJSON);
}
public T Configuration()

View File

@ -12,5 +12,6 @@ namespace SharedLibraryCore.Interfaces
void Build();
T Configuration();
void Set(T config);
string FileName { get; }
}
}

View File

@ -17,7 +17,7 @@ namespace SharedLibraryCore.Interfaces
void Restart();
ILogger GetLogger(long serverId);
IList<Server> GetServers();
IList<Command> GetCommands();
IList<IManagerCommand> GetCommands();
IList<Helpers.MessageToken> GetMessageTokens();
IList<EFClient> GetActiveClients();
IConfigurationHandler<ApplicationConfiguration> GetApplicationSettings();

View File

@ -0,0 +1,48 @@
using System.Threading.Tasks;
using static SharedLibraryCore.Database.Models.EFClient;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// Defines the basic properties of a command
/// </summary>
public interface IManagerCommand
{
/// <summary>
/// Executes the command
/// </summary>
/// <param name="gameEvent">event corresponding to the command</param>
/// <returns></returns>
Task ExecuteAsync(GameEvent gameEvent);
/// <summary>
/// Name of the command
/// </summary>
string Name { get; }
/// <summary>
/// Description of the command
/// </summary>
string Description { get; }
/// <summary>
/// Alternative name of the command
/// </summary>
string Alias { get; }
/// <summary>
/// Minimum permission required to execute the command
/// </summary>
Permission Permission { get; }
/// <summary>
/// Syntax for using the command
/// </summary>
string Syntax { get; }
/// <summary>
/// Indicates if target is required
/// </summary>
bool RequiresTarget { get; }
}
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Reflection;
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// Defines the capabilities of the plugin importer
/// </summary>
public interface IPluginImporter
{
/// <summary>
/// Command types that are defined in plugin assemblies
/// </summary>
IList<Type> CommandTypes { get; }
/// <summary>
/// The loaded plugins from plugin assemblies
/// </summary>
IList<IPlugin> ActivePlugins { get; }
/// <summary>
/// Assemblies that contain plugins
/// </summary>
IList<Assembly> PluginAssemblies { get; }
/// <summary>
/// All assemblies in the plugin folder
/// </summary>
IList<Assembly> Assemblies { get; }
/// <summary>
/// Loads in plugin assemblies and script plugins
/// </summary>
void Load();
}
}

View File

@ -0,0 +1,15 @@
namespace SharedLibraryCore.Interfaces
{
/// <summary>
/// Defines the translation lookup capabilities for DI
/// </summary>
public interface ITranslationLookup
{
/// <summary>
/// Allows indexing
/// </summary>
/// <param name="key">translation lookup key</param>
/// <returns></returns>
string this[string key] { get; }
}
}

View File

@ -1,25 +1,23 @@
using Newtonsoft.Json;
using System;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.Text;
namespace SharedLibraryCore.Localization
{
public class Layout
{
public string LocalizationName { get; set; }
public Index LocalizationIndex { get; set; }
public TranslationLookup LocalizationIndex { get; set; }
public Layout(Dictionary<string, string> set)
{
LocalizationIndex = new Index()
LocalizationIndex = new TranslationLookup()
{
Set = set
};
}
}
public class Index
public class TranslationLookup : ITranslationLookup
{
public Dictionary<string, string> Set { get; set; }

View File

@ -1,8 +1,10 @@
using SharedLibraryCore.Localization;
using Newtonsoft.Json.Converters;
using SharedLibraryCore.Localization;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;

View File

@ -10,7 +10,7 @@ using System.Threading.Tasks;
namespace SharedLibraryCore
{
class ScriptPlugin : IPlugin
public class ScriptPlugin : IPlugin
{
public string Name { get; set; }

View File

@ -6,7 +6,7 @@
<ApplicationIcon />
<StartupObject />
<PackageId>RaidMax.IW4MAdmin.SharedLibraryCore</PackageId>
<Version>2.2.5</Version>
<Version>2.2.6</Version>
<Authors>RaidMax</Authors>
<Company>Forever None</Company>
<Configurations>Debug;Release;Prerelease</Configurations>
@ -20,8 +20,8 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Description>Shared Library for IW4MAdmin</Description>
<AssemblyVersion>2.2.5.0</AssemblyVersion>
<FileVersion>2.2.5.0</FileVersion>
<AssemblyVersion>2.2.6.0</AssemblyVersion>
<FileVersion>2.2.6.0</FileVersion>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Prerelease|AnyCPU'">

View File

@ -3,6 +3,7 @@ using Microsoft.AspNetCore.Mvc;
using SharedLibraryCore;
using SharedLibraryCore.Dtos;
using SharedLibraryCore.Interfaces;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -10,9 +11,11 @@ namespace WebfrontCore.Controllers
{
public class HomeController : BaseController
{
public HomeController(IManager manager) : base(manager)
{
private readonly IPluginImporter _pluginImporter;
public HomeController(IManager manager, IPluginImporter importer) : base(manager)
{
_pluginImporter = importer;
}
public async Task<IActionResult> Index()
@ -65,7 +68,7 @@ namespace WebfrontCore.Controllers
var pluginType = _cmd.GetType().Assembly.GetTypes().FirstOrDefault(_type => _type.Assembly != excludedAssembly && typeof(IPlugin).IsAssignableFrom(_type));
return pluginType == null ?
Utilities.CurrentLocalization.LocalizationIndex["WEBFRONT_HELP_COMMAND_NATIVE"] :
SharedLibraryCore.Plugins.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
_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
})
.Select(_grp => (_grp.Key, _grp.AsEnumerable()));

View File

@ -1,4 +1,5 @@
using System.IO;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
@ -11,15 +12,17 @@ namespace WebfrontCore
public class Program
{
public static IManager Manager;
public static IServiceProvider ApplicationServiceProvider;
static void Main()
{
throw new System.Exception("Webfront core cannot be run as a standalone application");
throw new Exception("Webfront core cannot be run as a standalone application");
}
public static Task Init(IManager mgr, CancellationToken cancellationToken)
public static Task Init(IManager mgr, IServiceProvider existingServiceProvider, CancellationToken cancellationToken)
{
Manager = mgr;
ApplicationServiceProvider = existingServiceProvider;
var config = Manager.GetApplicationSettings().Configuration();
Manager.MiddlewareActionHandler.Register(null, new CustomCssAccentMiddlewareAction("#007ACC", "#fd7e14", config.WebfrontPrimaryColor, config.WebfrontSecondaryColor), "custom_css_accent");
return BuildWebHost().RunAsync(cancellationToken);

View File

@ -17,16 +17,6 @@ namespace WebfrontCore
{
public class Startup
{
public static IConfigurationRoot Configuration { get; private set; }
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddEnvironmentVariables();
Configuration = builder.Build();
}
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
@ -93,6 +83,7 @@ namespace WebfrontCore
#endif
services.AddSingleton(Program.Manager);
services.AddSingleton(Program.ApplicationServiceProvider.GetService(typeof(IPluginImporter)) as IPluginImporter);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.

View File

@ -1,4 +1,4 @@
@model IEnumerable<(string, IEnumerable<Command>)>
@model IEnumerable<(string, IEnumerable<SharedLibraryCore.Interfaces.IManagerCommand>)>
@{
var loc = SharedLibraryCore.Utilities.CurrentLocalization.LocalizationIndex;
}

View File

@ -1,16 +1,16 @@
Version 2.4:
Version 2.3:
-added "live radar" feature
-added chat message to server on server list view
-added recently connected players dropdown option on webfront
-added "dashboard" to home view with quick stats
-added ability to customize accent color and branding on webfront
-added flag button to client profile
-hid flagged status of users on webfront unless logged in
-added ability to specify permission levels per command
-added snap anticheat metric
Version 2.3:
-added configuration option to ignore bots
-updated anticheat slightly
-hides flagged status of users on webfront unless logged in
-updated projects to .NET Core 3.1
-lots more
Version 2.2: