diff --git a/Application/Application.csproj b/Application/Application.csproj index 9abb26c6..0f73533c 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -5,7 +5,7 @@ netcoreapp3.1 false RaidMax.IW4MAdmin.Application - 2.3.1.0 + 2.3.2.0 RaidMax Forever None IW4MAdmin diff --git a/Application/ApplicationManager.cs b/Application/ApplicationManager.cs index e0b390af..efe53f30 100644 --- a/Application/ApplicationManager.cs +++ b/Application/ApplicationManager.cs @@ -256,9 +256,25 @@ namespace IW4MAdmin.Application if (plugin is ScriptPlugin scriptPlugin) { await scriptPlugin.Initialize(this); + scriptPlugin.Watcher.Changed += async (sender, e) => + { + try + { + await scriptPlugin.Initialize(this); + } + + catch (Exception ex) + { + Logger.WriteError(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(scriptPlugin.Name)); + Logger.WriteDebug(ex.Message); + } + }; } - await plugin.OnLoadAsync(this); + else + { + await plugin.OnLoadAsync(this); + } } catch (Exception ex) diff --git a/Application/IW4MServer.cs b/Application/IW4MServer.cs index 0f2f3f28..6afdb110 100644 --- a/Application/IW4MServer.cs +++ b/Application/IW4MServer.cs @@ -881,9 +881,9 @@ namespace IW4MAdmin Version = RconParser.Version; } - var svRunning = await this.GetDvarAsync("sv_running"); + var svRunning = await this.GetDvarAsync("sv_running"); - if (svRunning.Value == 0) + if (!string.IsNullOrEmpty(svRunning.Value) && svRunning.Value != "1") { throw new ServerException(loc["SERVER_ERROR_NOT_RUNNING"]); } diff --git a/Application/Main.cs b/Application/Main.cs index 9eea2d7d..f49ea19a 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -8,6 +8,7 @@ using SharedLibraryCore.Exceptions; using SharedLibraryCore.Helpers; using SharedLibraryCore.Interfaces; using System; +using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; @@ -68,30 +69,9 @@ namespace IW4MAdmin.Application try { var services = ConfigureServices(); - - using (var builder = services.BuildServiceProvider()) - { - translationLookup = builder.GetRequiredService(); - var importer = builder.GetRequiredService(); - 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); - } - } - serviceProvider = services.BuildServiceProvider(); - var pluginImporter = serviceProvider.GetRequiredService(); - pluginImporter.Load(); - ServerManager = (ApplicationManager)serviceProvider.GetRequiredService(); + translationLookup = serviceProvider.GetRequiredService(); // do any needed housekeeping file/folder migrations ConfigurationMigration.MoveConfigFolder10518(null); @@ -283,8 +263,8 @@ namespace IW4MAdmin.Application /// private static IServiceCollection ConfigureServices() { - var serviceProvider = new ServiceCollection(); - serviceProvider.AddSingleton() + var serviceCollection = new ServiceCollection(); + serviceCollection.AddSingleton(_serviceProvider => serviceCollection) .AddSingleton(new BaseConfigurationHandler("IW4MAdminSettings") as IConfigurationHandler) .AddSingleton(new BaseConfigurationHandler("CommandConfiguration") as IConfigurationHandler) .AddSingleton(_serviceProvider => _serviceProvider.GetRequiredService>().Configuration()) @@ -292,14 +272,27 @@ namespace IW4MAdmin.Application .AddSingleton(_serviceProvider => new Logger("IW4MAdmin-Manager")) .AddSingleton() .AddSingleton() + .AddTransient(_serviceProvider => + { + var importer = _serviceProvider.GetRequiredService(); + var config = _serviceProvider.GetRequiredService(); + var layout = _serviceProvider.GetRequiredService(); + + // 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 => { var config = _serviceProvider.GetRequiredService>().Configuration(); return Localization.Configure.Initialize(useLocalTranslation: config?.UseLocalTranslations ?? false, customLocale: config?.EnableCustomLocale ?? false ? (config.CustomLocale ?? "en-US") : "en-US"); - }); + }) + .AddSingleton(); - return serviceProvider; + return serviceCollection; } } } diff --git a/Application/Misc/PluginImporter.cs b/Application/Misc/PluginImporter.cs index 3231ad7d..cb6c73e1 100644 --- a/Application/Misc/PluginImporter.cs +++ b/Application/Misc/PluginImporter.cs @@ -22,12 +22,14 @@ namespace IW4MAdmin.Application.Helpers { _logger = logger; _translationLookup = translationLookup; + + Load(); } /// /// Loads all the assembly and javascript plugins /// - public void Load() + private void Load() { string pluginDir = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}"; string[] dllFileNames = null; diff --git a/SharedLibraryCore/Commands/NativeCommands.cs b/SharedLibraryCore/Commands/NativeCommands.cs index 2cf2d717..09da50e6 100644 --- a/SharedLibraryCore/Commands/NativeCommands.cs +++ b/SharedLibraryCore/Commands/NativeCommands.cs @@ -1315,7 +1315,7 @@ namespace SharedLibraryCore.Commands /// /// Lists the loaded plugins /// - public class ListPluginsCommand : Command + /*public class ListPluginsCommand : Command { private readonly IPluginImporter _pluginImporter; public ListPluginsCommand(CommandConfiguration config, ITranslationLookup translationLookup, IPluginImporter pluginImporter) : base(config, translationLookup) @@ -1337,7 +1337,7 @@ namespace SharedLibraryCore.Commands } return Task.CompletedTask; } - } + }*/ /// /// Lists external IP diff --git a/SharedLibraryCore/Interfaces/IPluginImporter.cs b/SharedLibraryCore/Interfaces/IPluginImporter.cs index 0e7d45ad..7403ce14 100644 --- a/SharedLibraryCore/Interfaces/IPluginImporter.cs +++ b/SharedLibraryCore/Interfaces/IPluginImporter.cs @@ -28,10 +28,5 @@ namespace SharedLibraryCore.Interfaces /// All assemblies in the plugin folder /// IList Assemblies { get; } - - /// - /// Loads in plugin assemblies and script plugins - /// - void Load(); } } diff --git a/SharedLibraryCore/ScriptPlugin.cs b/SharedLibraryCore/ScriptPlugin.cs index f7279b69..c26e1461 100644 --- a/SharedLibraryCore/ScriptPlugin.cs +++ b/SharedLibraryCore/ScriptPlugin.cs @@ -18,46 +18,110 @@ namespace SharedLibraryCore public string Author { get; set; } + public FileSystemWatcher Watcher { get; private set; } + private Engine ScriptEngine; - private readonly string FileName; - private IManager Manager; - private readonly FileSystemWatcher _watcher; + private readonly string _fileName; private readonly SemaphoreSlim _fileChanging; private bool successfullyLoaded; - public ScriptPlugin(string fileName) + public ScriptPlugin(string filename) { - FileName = fileName; - _fileChanging = new SemaphoreSlim(1, 1); - _watcher = new FileSystemWatcher() + _fileName = filename; + Watcher = new FileSystemWatcher() { Path = $"{Utilities.OperatingDirectory}Plugins{Path.DirectorySeparatorChar}", NotifyFilter = NotifyFilters.Size, - Filter = fileName.Split(Path.DirectorySeparatorChar).Last() + Filter = _fileName.Split(Path.DirectorySeparatorChar).Last() }; - _watcher.Changed += Watcher_Changed; - _watcher.EnableRaisingEvents = true; + Watcher.EnableRaisingEvents = true; + _fileChanging = new SemaphoreSlim(1, 1); } ~ScriptPlugin() { - _watcher.Dispose(); + Watcher.Dispose(); _fileChanging.Dispose(); } - private async void Watcher_Changed(object sender, FileSystemEventArgs e) + + public async Task Initialize(IManager manager) { + await _fileChanging.WaitAsync(); + try { - await _fileChanging.WaitAsync(); - await Initialize(Manager); + // for some reason we get an event trigger when the file is not finished being modified. + // this must have been a change in .NET CORE 3.x + // so if the new file is empty we can't process it yet + if (new FileInfo(_fileName).Length == 0L) + { + return; + } + + bool firstRun = ScriptEngine == null; + + // it's been loaded before so we need to call the unload event + if (!firstRun) + { + await OnUnloadAsync(); + } + + successfullyLoaded = false; + string script; + + using (var stream = new FileStream(_fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + { + using (var reader = new StreamReader(stream, Encoding.Default)) + { + script = await reader.ReadToEndAsync(); + } + } + + ScriptEngine = new Engine(cfg => + cfg.AllowClr(new[] + { + typeof(System.Net.Http.HttpClient).Assembly, + typeof(EFClient).Assembly, + typeof(Utilities).Assembly, + typeof(Encoding).Assembly + }) + .CatchClrExceptions()); + + ScriptEngine.Execute(script); + ScriptEngine.SetValue("_localization", Utilities.CurrentLocalization); + dynamic pluginObject = ScriptEngine.GetValue("plugin").ToObject(); + + Author = pluginObject.author; + Name = pluginObject.name; + Version = (float)pluginObject.version; + + try + { + if (pluginObject.isParser) + { + await OnLoadAsync(manager); + IEventParser eventParser = (IEventParser)ScriptEngine.GetValue("eventParser").ToObject(); + IRConParser rconParser = (IRConParser)ScriptEngine.GetValue("rconParser").ToObject(); + manager.AdditionalEventParsers.Add(eventParser); + manager.AdditionalRConParsers.Add(rconParser); + } + } + catch { } + + + if (!firstRun) + { + await OnLoadAsync(manager); + } + + successfullyLoaded = true; } - catch (Exception ex) + catch { - Manager.GetLogger(0).WriteError(Utilities.CurrentLocalization.LocalizationIndex["PLUGIN_IMPORTER_ERROR"].FormatExt(Name)); - Manager.GetLogger(0).WriteDebug(ex.Message); + throw; } finally @@ -69,104 +133,20 @@ namespace SharedLibraryCore } } - public async Task Initialize(IManager mgr) - { - // for some reason we get an event trigger when the file is not finished being modified. - // this must have been a change in .NET CORE 3.x - // so if the new file is empty we can't process it yet - if (new FileInfo(FileName).Length == 0L) - { - return; - } - - bool firstRun = ScriptEngine == null; - - // it's been loaded before so we need to call the unload event - if (!firstRun) - { - await OnUnloadAsync(); - } - - successfullyLoaded = false; - Manager = mgr; - string script; - - using (var stream = new FileStream(FileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - { - using (var reader = new StreamReader(stream, Encoding.Default)) - { - script = await reader.ReadToEndAsync(); - } - } - - ScriptEngine = new Engine(cfg => - cfg.AllowClr(new[] - { - typeof(System.Net.Http.HttpClient).Assembly, - typeof(EFClient).Assembly, - typeof(Utilities).Assembly, - typeof(Encoding).Assembly - }) - .CatchClrExceptions()); - - ScriptEngine.Execute(script); - ScriptEngine.SetValue("_localization", Utilities.CurrentLocalization); - dynamic pluginObject = ScriptEngine.GetValue("plugin").ToObject(); - - Author = pluginObject.author; - Name = pluginObject.name; - Version = (float)pluginObject.version; - - try - { - if(pluginObject.isParser) - { - await OnLoadAsync(mgr); - IEventParser eventParser = (IEventParser)ScriptEngine.GetValue("eventParser").ToObject(); - IRConParser rconParser = (IRConParser)ScriptEngine.GetValue("rconParser").ToObject(); - Manager.AdditionalEventParsers.Add(eventParser); - Manager.AdditionalRConParsers.Add(rconParser); - } - } - catch { } - - - if (!firstRun) - { - await OnLoadAsync(mgr); - } - - successfullyLoaded = true; - } - public async Task OnEventAsync(GameEvent E, Server S) { if (successfullyLoaded) { - try - { - await _fileChanging.WaitAsync(); - ScriptEngine.SetValue("_gameEvent", E); - ScriptEngine.SetValue("_server", S); - ScriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S)); - ScriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue(); - } - - catch { } - - finally - { - if (_fileChanging.CurrentCount == 0) - { - _fileChanging.Release(1); - } - } + ScriptEngine.SetValue("_gameEvent", E); + ScriptEngine.SetValue("_server", S); + ScriptEngine.SetValue("_IW4MAdminClient", Utilities.IW4MAdminClient(S)); + await Task.FromResult(ScriptEngine.Execute("plugin.onEventAsync(_gameEvent, _server)").GetCompletionValue()); } } public Task OnLoadAsync(IManager manager) { - Manager.GetLogger(0).WriteDebug($"OnLoad executing for {Name}"); + manager.GetLogger(0).WriteDebug($"OnLoad executing for {Name}"); ScriptEngine.SetValue("_manager", manager); return Task.FromResult(ScriptEngine.Execute("plugin.onLoadAsync(_manager)").GetCompletionValue()); } @@ -181,7 +161,6 @@ namespace SharedLibraryCore { if (successfullyLoaded) { - Manager.GetLogger(0).WriteDebug($"OnUnLoad executing for {Name}"); await Task.FromResult(ScriptEngine.Execute("plugin.onUnloadAsync()").GetCompletionValue()); } } diff --git a/WebfrontCore/Controllers/ServerController.cs b/WebfrontCore/Controllers/ServerController.cs index f73506c6..3c1fc308 100644 --- a/WebfrontCore/Controllers/ServerController.cs +++ b/WebfrontCore/Controllers/ServerController.cs @@ -21,7 +21,7 @@ namespace WebfrontCore.Controllers if (s == null) { - return View("Error", "Invalid server!"); + return NotFound(); } var serverInfo = new ServerInfo()