using System.Reflection; using EggLink.DanhengServer.GameServer.Plugin.Constructor; using EggLink.DanhengServer.Internationalization; using EggLink.DanhengServer.Util; using McMaster.NETCore.Plugins; namespace EggLink.DanhengServer.GameServer.Plugin; public class PluginManager { private static readonly Logger Logger = new("PluginManager"); public static readonly Dictionary Plugins = []; public static readonly Dictionary> PluginAssemblies = []; public static List GetPluginAssemblies() { var assemblies = new List(); foreach (var plugin in Plugins.Keys) if (PluginAssemblies.TryGetValue(plugin, out var value)) assemblies.AddRange(value); return assemblies; } #region Plugin Manager public static void LoadPlugins() { // get all the plugins in the plugin directory if (!Directory.Exists(ConfigManager.Config.Path.PluginPath)) Directory.CreateDirectory(ConfigManager.Config.Path.PluginPath); var plugins = Directory.GetFiles(ConfigManager.Config.Path.PluginPath, "*.dll"); AppDomain.CurrentDomain.AssemblyResolve += (_, args) => { var assemblyName = new AssemblyName(args.Name).Name + ".dll"; var assemblyPath = Path.Combine(ConfigManager.Config.Path.PluginPath, assemblyName); return File.Exists(assemblyPath) ? Assembly.LoadFrom(assemblyPath) : null; }; foreach (var plugin in plugins) { var fileInfo = new FileInfo(plugin); LoadPlugin(fileInfo.FullName); } //var dict = PluginAssemblies.ToDictionary(pluginAssembly => Plugins[pluginAssembly.Key].Name, pluginAssembly => pluginAssembly.Value); //I18NManager.LoadPluginLanguage(dict); } public static void LoadPlugin(string plugin) { try { var config = new PluginConfig(plugin) { PreferSharedTypes = true, LoadInMemory = true }; var loader = new PluginLoader(config); var assembly = loader.LoadDefaultAssembly(); var types = assembly.GetTypes(); foreach (var type in types) if (typeof(IPlugin).IsAssignableFrom(type)) { if (Activator.CreateInstance(type) is IPlugin pluginInstance) { var pluginInfo = type.GetCustomAttribute(); if (pluginInfo != null) { Logger.Info( $"Loaded plugin {pluginInfo.Name} v{pluginInfo.Version}: {pluginInfo.Description}"); } else { Logger.Info($"Loaded plugin {plugin}: No plugin info"); continue; } if (Plugins.Values.Any(p => p.Name == pluginInfo.Name)) { Logger.Error($"Failed to load plugin {plugin}: Plugin already loaded"); continue; } Plugins.Add(pluginInstance, pluginInfo); if (!PluginAssemblies.TryGetValue(pluginInstance, out var pluginTypes)) { pluginTypes = []; PluginAssemblies[pluginInstance] = pluginTypes; } pluginTypes.AddRange(types); try { var dict = new Dictionary> { { pluginInfo.Name, pluginTypes } }; I18NManager.LoadPluginLanguage(dict); pluginInstance.OnLoad(); } catch (Exception e) { Logger.Error($"Failed to load plugin {plugin}: {e.Message}"); // unload the plugin UnloadPlugin(pluginInstance); } } else { Logger.Error($"Failed to load plugin {plugin}: Plugin instance is null"); } } } catch (Exception ex) { Logger.Error($"Failed to load plugin {plugin}: {ex.Message}"); } } public static void UnloadPlugin(IPlugin plugin) { if (!Plugins.TryGetValue(plugin, out var value)) return; plugin.OnUnload(); Plugins.Remove(plugin); PluginAssemblies.Remove(plugin); I18NManager.PluginLanguages.Remove(value.Name); Logger.Info($"Unloaded plugin {value.Name}"); } public static void UnloadPlugins() { foreach (var plugin in Plugins.Keys) UnloadPlugin(plugin); Logger.Info(I18NManager.Translate("Server.ServerInfo.UnloadedItems", I18NManager.Translate("Word.Plugin"))); } #endregion }