Compare commits

...

1 Commits

Author SHA1 Message Date
roflmuffin
2535ac0575 feat: add assembly name lazy loading of shared libraries 2024-03-04 12:15:37 +10:00
4 changed files with 61 additions and 24 deletions

View File

@@ -130,11 +130,11 @@ namespace CounterStrikeSharp.API.Core
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();
sb.AppendFormat(" [#{0}:{1}]: \"{2}\" ({3})", plugin.PluginId, sb.AppendFormat(" [#{0}:{1}]: \"{2}\" ({3})", plugin.PluginId,
plugin.State.ToString().ToUpper(), plugin.Plugin.ModuleName, plugin.State.ToString().ToUpper(), plugin.Plugin?.ModuleName ?? "Unknown",
plugin.Plugin.ModuleVersion); plugin.Plugin?.ModuleVersion ?? "Unknown");
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleAuthor)) if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleAuthor))
sb.AppendFormat(" by {0}", plugin.Plugin.ModuleAuthor); sb.AppendFormat(" by {0}", plugin.Plugin.ModuleAuthor);
if (!string.IsNullOrEmpty(plugin.Plugin.ModuleDescription)) if (!string.IsNullOrEmpty(plugin.Plugin?.ModuleDescription))
{ {
sb.Append("\n"); sb.Append("\n");
sb.Append(" "); sb.Append(" ");

View File

@@ -1,8 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Runtime.Loader; using System.Runtime.Loader;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Core.Commands; using CounterStrikeSharp.API.Core.Commands;
using CounterStrikeSharp.API.Core.Hosting; using CounterStrikeSharp.API.Core.Hosting;
using McMaster.NETCore.Plugins;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@@ -15,8 +18,11 @@ public class PluginManager : IPluginManager
private readonly ICommandManager _commandManager; private readonly ICommandManager _commandManager;
private readonly IServiceProvider _serviceProvider; private readonly IServiceProvider _serviceProvider;
private readonly ILogger<PluginManager> _logger; private readonly ILogger<PluginManager> _logger;
private readonly Dictionary<string, Assembly> _sharedAssemblies = new();
private bool _loadedSharedLibs = false;
public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager, ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory) public PluginManager(IScriptHostConfiguration scriptHostConfiguration, ICommandManager commandManager,
ILogger<PluginManager> logger, IServiceProvider serviceProvider, IServiceScopeFactory serviceScopeFactory)
{ {
_scriptHostConfiguration = scriptHostConfiguration; _scriptHostConfiguration = scriptHostConfiguration;
_commandManager = commandManager; _commandManager = commandManager;
@@ -24,6 +30,36 @@ public class PluginManager : IPluginManager
_serviceProvider = serviceProvider; _serviceProvider = serviceProvider;
} }
private void LoadLibrary(string path)
{
var loader = PluginLoader.CreateFromAssemblyFile(path, new[] { typeof(IPlugin), typeof(PluginCapability<>), typeof(PlayerCapability<>) },
config => { config.PreferSharedTypes = true; });
var assembly = loader.LoadDefaultAssembly();
_sharedAssemblies[assembly.GetName().FullName] = assembly;
}
private void LoadSharedLibraries()
{
var sharedDirectory = Directory.GetDirectories(_scriptHostConfiguration.SharedPath);
var sharedAssemblyPaths = sharedDirectory
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists)
.ToArray();
foreach (var sharedAssemblyPath in sharedAssemblyPaths)
{
try
{
LoadLibrary(sharedAssemblyPath);
}
catch (Exception e)
{
_logger.LogError(e, "Failed to load shared assembly from {Path}", sharedAssemblyPath);
}
}
}
public void Load() public void Load()
{ {
var pluginDirectories = Directory.GetDirectories(_scriptHostConfiguration.PluginPath); var pluginDirectories = Directory.GetDirectories(_scriptHostConfiguration.PluginPath);
@@ -31,23 +67,23 @@ public class PluginManager : IPluginManager
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll")) .Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists) .Where(File.Exists)
.ToArray(); .ToArray();
var sharedDirectory = Directory.GetDirectories(_scriptHostConfiguration.SharedPath);
var sharedAssemblyPaths = sharedDirectory
.Select(dir => Path.Combine(dir, Path.GetFileName(dir) + ".dll"))
.Where(File.Exists)
.ToArray();
foreach (var sharedAssemblyPath in sharedAssemblyPaths) AssemblyLoadContext.Default.Resolving += (context, name) =>
{ {
try if (!_loadedSharedLibs)
{ {
AssemblyLoadContext.Default.LoadFromAssemblyPath(sharedAssemblyPath); LoadSharedLibraries();
} catch (Exception e) _loadedSharedLibs = true;
{
_logger.LogError(e, "Failed to load shared assembly from {Path}", sharedAssemblyPath);
} }
}
if (!_sharedAssemblies.TryGetValue(name.FullName, out var assembly))
{
_logger.LogError("Failed to use existing shared assembly: {Name}", name);
return null;
}
return assembly;
};
foreach (var path in pluginAssemblyPaths) foreach (var path in pluginAssemblyPaths)
{ {
@@ -60,7 +96,7 @@ public class PluginManager : IPluginManager
_logger.LogError(e, "Failed to load plugin from {Path}", path); _logger.LogError(e, "Failed to load plugin from {Path}", path);
} }
} }
foreach (var plugin in _loadedPluginContexts) foreach (var plugin in _loadedPluginContexts)
{ {
plugin.Plugin.OnAllPluginsLoaded(false); plugin.Plugin.OnAllPluginsLoaded(false);
@@ -74,7 +110,8 @@ public class PluginManager : IPluginManager
public void LoadPlugin(string path) public void LoadPlugin(string path)
{ {
var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path, _loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1); var plugin = new PluginContext(_serviceProvider, _commandManager, _scriptHostConfiguration, path,
_loadedPluginContexts.Select(x => x.PluginId).DefaultIfEmpty(0).Max() + 1);
_loadedPluginContexts.Add(plugin); _loadedPluginContexts.Add(plugin);
plugin.Load(); plugin.Load();
} }

View File

@@ -18,6 +18,7 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
using CounterStrikeSharp.API.Core.Attributes; using CounterStrikeSharp.API.Core.Attributes;
using CounterStrikeSharp.API.Core.Capabilities;
using CounterStrikeSharp.API.Core.Commands; using CounterStrikeSharp.API.Core.Commands;
using CounterStrikeSharp.API.Core.Hosting; using CounterStrikeSharp.API.Core.Hosting;
using CounterStrikeSharp.API.Core.Logging; using CounterStrikeSharp.API.Core.Logging;
@@ -76,7 +77,7 @@ namespace CounterStrikeSharp.API.Core.Plugin
config.IsUnloadable = true; config.IsUnloadable = true;
config.PreferSharedTypes = true; config.PreferSharedTypes = true;
}); });
if (CoreConfig.PluginHotReloadEnabled) if (CoreConfig.PluginHotReloadEnabled)
{ {
_fileWatcher = new FileSystemWatcher _fileWatcher = new FileSystemWatcher
@@ -119,12 +120,12 @@ namespace CounterStrikeSharp.API.Core.Plugin
public void Load(bool hotReload = false) public void Load(bool hotReload = false)
{ {
if (State == PluginState.Loaded) return; if (State == PluginState.Loaded) return;
using (Loader.EnterContextualReflection()) using (Loader.EnterContextualReflection())
{ {
var defaultAssembly = Loader.LoadDefaultAssembly(); var defaultAssembly = Loader.LoadDefaultAssembly();
Type pluginType = defaultAssembly.GetTypes() Type pluginType = defaultAssembly.GetExportedTypes()
.FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t)); .FirstOrDefault(t => typeof(IPlugin).IsAssignableFrom(t));
if (pluginType == null) throw new Exception("Unable to find plugin in assembly"); if (pluginType == null) throw new Exception("Unable to find plugin in assembly");

View File

@@ -132,7 +132,6 @@ namespace CounterStrikeSharp.API.Modules.Admin
foreach (var adminDef in adminsFromFile.Values) foreach (var adminDef in adminsFromFile.Values)
{ {
adminDef.InitalizeFlags(); adminDef.InitalizeFlags();
Console.WriteLine($"Domains: {adminDef.Flags.Count}");
if (SteamID.TryParse(adminDef.Identity, out var steamId)) if (SteamID.TryParse(adminDef.Identity, out var steamId))
{ {