From e956ab4d1ae145702fd4595b9443079b54c74675 Mon Sep 17 00:00:00 2001 From: RaidMax Date: Tue, 27 Aug 2024 10:10:58 -0500 Subject: [PATCH] support hot reload of plugin code in debug configuration --- Application/Application.csproj | 5 ++-- Application/Main.cs | 7 +++++ Application/Plugin/PluginImporter.cs | 25 ++++++++++++++--- IW4MAdmin.sln | 28 +++++++++++++++++++ .../PluginDebugReference.csproj | 19 +++++++++++++ PluginDebugReference/StrongReference.cs | 10 +++++++ 6 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 PluginDebugReference/PluginDebugReference.csproj create mode 100644 PluginDebugReference/StrongReference.cs diff --git a/Application/Application.csproj b/Application/Application.csproj index cfade8ce..06691df5 100644 --- a/Application/Application.csproj +++ b/Application/Application.csproj @@ -55,6 +55,7 @@ + true @@ -73,8 +74,8 @@ - + diff --git a/Application/Main.cs b/Application/Main.cs index 0362688b..3299799e 100644 --- a/Application/Main.cs +++ b/Application/Main.cs @@ -38,6 +38,9 @@ using ILogger = Microsoft.Extensions.Logging.ILogger; using IW4MAdmin.Plugins.Stats.Client.Abstractions; using IW4MAdmin.Plugins.Stats.Client; using Microsoft.Extensions.Hosting; +#if DEBUG +using PluginDebugReference; +#endif using Refit; using SharedLibraryCore.Interfaces.Events; using Stats.Client.Abstractions; @@ -129,6 +132,10 @@ namespace IW4MAdmin.Application Utilities.DefaultLogger = logger; logger.LogInformation("Begin IW4MAdmin startup. Version is {Version}", Version); + #if DEBUG + StrongReferencesLoader.Load(); + #endif + try { // do any needed housekeeping file/folder migrations diff --git a/Application/Plugin/PluginImporter.cs b/Application/Plugin/PluginImporter.cs index df3a54f6..2d221279 100644 --- a/Application/Plugin/PluginImporter.cs +++ b/Application/Plugin/PluginImporter.cs @@ -5,6 +5,9 @@ using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using IW4MAdmin.Application.API.Master; +#if DEBUG +using Microsoft.Extensions.DependencyModel; +#endif using Microsoft.Extensions.Logging; using SharedLibraryCore; using SharedLibraryCore.Configuration; @@ -102,13 +105,27 @@ namespace IW4MAdmin.Application.Plugin return (pluginTypes, commandTypes, configurationTypes); } +#if DEBUG + var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies().Select(asm => asm.GetName().Name); + var references = DependencyContext.Default?.GetDefaultAssemblyNames().Select(x => x.Name) ?? []; + var validAssemblies = references.Except(loadedAssemblies) + .Where(asm => !asm.StartsWith("Microsoft")) + .Where(asm => !asm.StartsWith("System")) + .Distinct(); + var reloadableAssemblies = validAssemblies.Select(Assembly.Load); +#endif + // we only want to load the most recent assembly in case of duplicates - var assemblies = dllFileNames.Select(Assembly.LoadFrom) + var assemblies = dllFileNames.Select(fileName => fileName).Select(Assembly.LoadFrom) .Union(GetRemoteAssemblies()) +#if DEBUG + .Union(reloadableAssemblies) +#endif .GroupBy(assembly => assembly.FullName).Select(assembly => assembly.OrderByDescending(asm => asm.GetName().Version).First()); - var eligibleAssemblyTypes = assemblies + var eligibleAssemblyTypes = assemblies.Concat(AppDomain.CurrentDomain.GetAssemblies() + .Where(asm => !new[] { "IW4MAdmin", "SharedLibraryCore" }.Contains(asm.GetName().Name))) .SelectMany(asm => { try @@ -122,7 +139,7 @@ namespace IW4MAdmin.Application.Plugin }).Where(type => FilterTypes.Any(filterType => type.GetInterface(filterType.Name, false) != null) || (type.IsClass && FilterTypes.Contains(type.BaseType))); - + foreach (var assemblyType in eligibleAssemblyTypes) { var isPlugin = @@ -193,7 +210,7 @@ namespace IW4MAdmin.Application.Plugin catch (Exception ex) { - _logger.LogWarning(ex,"Could not load remote scripts"); + _logger.LogWarning(ex, "Could not load remote scripts"); return Enumerable.Empty(); } } diff --git a/IW4MAdmin.sln b/IW4MAdmin.sln index bf0f6aeb..55213499 100644 --- a/IW4MAdmin.sln +++ b/IW4MAdmin.sln @@ -52,6 +52,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ScriptPlugins", "ScriptPlug Plugins\ScriptPlugins\ServerBanner.js = Plugins\ScriptPlugins\ServerBanner.js Plugins\ScriptPlugins\ParserBOIII.js = Plugins\ScriptPlugins\ParserBOIII.js Plugins\ScriptPlugins\ParserL4D2SM.js = Plugins\ScriptPlugins\ParserL4D2SM.js + Plugins\ScriptPlugins\ParserPlutoniumT6.js = Plugins\ScriptPlugins\ParserPlutoniumT6.js + Plugins\ScriptPlugins\ParserH2MMOD.js = Plugins\ScriptPlugins\ParserH2MMOD.js EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutomessageFeed", "Plugins\AutomessageFeed\AutomessageFeed.csproj", "{F5815359-CFC7-44B4-9A3B-C04BACAD5836}" @@ -115,6 +117,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GithubActions", "GithubActi .github\workflows\shared_library_nuget.yml = .github\workflows\shared_library_nuget.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PluginDebugReference", "PluginDebugReference\PluginDebugReference.csproj", "{2EB30B9B-FEC4-4A30-A955-515D55D0555B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -443,6 +447,30 @@ Global {259824F3-D860-4233-91D6-FF73D4DD8B18}.Release|x86.Build.0 = Release|Any CPU {259824F3-D860-4233-91D6-FF73D4DD8B18}.Prerelease|Any CPU.ActiveCfg = Prerelease|Any CPU {259824F3-D860-4233-91D6-FF73D4DD8B18}.Prerelease|Any CPU.Build.0 = Prerelease|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|x64.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|x64.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|x86.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Debug|x86.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|Any CPU.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|Any CPU.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|Mixed Platforms.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|x64.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|x64.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|x86.ActiveCfg = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Prerelease|x86.Build.0 = Debug|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|Any CPU.Build.0 = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|x64.ActiveCfg = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|x64.Build.0 = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|x86.ActiveCfg = Release|Any CPU + {2EB30B9B-FEC4-4A30-A955-515D55D0555B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/PluginDebugReference/PluginDebugReference.csproj b/PluginDebugReference/PluginDebugReference.csproj new file mode 100644 index 00000000..b263a6ac --- /dev/null +++ b/PluginDebugReference/PluginDebugReference.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/PluginDebugReference/StrongReference.cs b/PluginDebugReference/StrongReference.cs new file mode 100644 index 00000000..579e8e2a --- /dev/null +++ b/PluginDebugReference/StrongReference.cs @@ -0,0 +1,10 @@ + +namespace PluginDebugReference; + +public static class StrongReferencesLoader +{ + public static void Load() + { + // only for explicit reference + } +}