// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using System.Reflection; namespace FlaxEngine { /// /// Plugin related event delegate type. /// /// The plugin. [HideInEditor] public delegate void PluginDelegate(Plugin plugin); public static partial class PluginManager { private static readonly List _gamePlugins = new List(); private static readonly List _editorPlugins = new List(); /// /// Gets the game plugins. /// public static IReadOnlyList GamePlugins => _gamePlugins; /// /// Gets the editor plugins. /// public static IReadOnlyList EditorPlugins => _editorPlugins; /// /// Occurs before loading plugin. /// public static event PluginDelegate PluginLoading; /// /// Occurs when plugin gets loaded and initialized. /// public static event PluginDelegate PluginLoaded; /// /// Occurs before unloading plugin. /// public static event PluginDelegate PluginUnloading; /// /// Occurs when plugin gets unloaded. It should not be used anymore. /// public static event PluginDelegate PluginUnloaded; /// /// Occurs when plugins collection gets edited (added or removed plugin). /// public static event Action PluginsChanged; /// /// Determines whether can load the specified plugin. /// /// The plugin description. /// True if load it, otherwise false. public delegate bool CanLoadPluginDelegate(ref PluginDescription pluginDesc); /// /// Determines whether can load the specified plugin. /// public static CanLoadPluginDelegate CanLoadPlugin = DefaultCanLoadPlugin; /// /// Plugin related event delegate type. /// /// The plugin. public delegate void PluginDelegate(Plugin plugin); /// /// The default implementation for . /// /// The plugin description. /// True if load it, otherwise false. public static bool DefaultCanLoadPlugin(ref PluginDescription pluginDesc) { return true; //return !pluginDesc.DisabledByDefault; } #if FLAX_EDITOR internal static void InitializeGamePlugins() { Profiler.BeginEvent("PluginManager.InitializeGamePlugins"); for (var i = 0; i < _gamePlugins.Count; i++) { InvokeInitialize(_gamePlugins[i]); } Profiler.EndEvent(); } internal static void DeinitializeGamePlugins() { Profiler.BeginEvent("PluginManager.DeinitializeGamePlugins"); for (var i = _gamePlugins.Count - 1; i >= 0; i--) { InvokeDeinitialize(_gamePlugins[i]); } Profiler.EndEvent(); } #endif private static void InvokeInitialize(Plugin plugin) { if (plugin._initialized) return; try { Debug.Write(LogType.Info, "Loading plugin " + plugin); PluginLoading?.Invoke(plugin); plugin.Initialize(); plugin._initialized = true; PluginLoaded?.Invoke(plugin); } catch (Exception ex) { Debug.LogException(ex); Debug.LogErrorFormat("Failed to initialize plugin {0}. {1}", plugin, ex.Message); } } private static void InvokeDeinitialize(Plugin plugin) { if (!plugin._initialized) return; try { Debug.Write(LogType.Info, "Unloading plugin " + plugin); PluginUnloading?.Invoke(plugin); plugin.Deinitialize(); plugin._initialized = false; PluginUnloaded?.Invoke(plugin); } catch (Exception ex) { Debug.LogException(ex); Debug.LogErrorFormat("Failed to deinitialize plugin {0}. {1}", plugin, ex.Message); } } /// /// Returns the first plugin of the provided type. /// /// The plugin type. /// The plugin, or null if not loaded. public static T GetPlugin() where T : class { foreach (Plugin p in _editorPlugins) { if (p.GetType() == typeof(T)) return (T)Convert.ChangeType(p, typeof(T)); } foreach (GamePlugin gp in _gamePlugins) { if (gp.GetType() == typeof(T)) return (T)Convert.ChangeType(gp, typeof(T)); } return null; } /// /// Return the first plugin using the provided name. /// /// /// The plugin name. /// The plugin, or null if not loaded. public static Plugin GetPlugin(string name) { foreach (Plugin p in _editorPlugins) { if (p.Description.Name.Equals(name)) return p; } foreach (GamePlugin gp in _gamePlugins) { if (gp.Description.Name.Equals(name)) return gp; } return null; } internal static void Internal_LoadPlugin(Type type, bool isEditor) { if (type == null) throw new ArgumentNullException(); // Create and check if use it var plugin = (Plugin)Activator.CreateInstance(type); var desc = plugin.Description; if (!CanLoadPlugin(ref desc)) { Debug.Write(LogType.Info, "Skip loading plugin " + plugin); return; } if (!isEditor) { _gamePlugins.Add((GamePlugin)plugin); #if !FLAX_EDITOR InvokeInitialize(plugin); #endif } #if FLAX_EDITOR else { _editorPlugins.Add(plugin); InvokeInitialize(plugin); } #endif PluginsChanged?.Invoke(); } internal static void Internal_Dispose(Assembly assembly) { bool changed = false; for (int i = _editorPlugins.Count - 1; i >= 0 && _editorPlugins.Count > 0; i--) { var plugin = _editorPlugins[i]; if (plugin.GetType().Assembly == assembly) { InvokeDeinitialize(plugin); _editorPlugins.RemoveAt(i); changed = true; } } for (int i = _gamePlugins.Count - 1; i >= 0 && _gamePlugins.Count > 0; i--) { var plugin = _gamePlugins[i]; if (plugin.GetType().Assembly == assembly) { InvokeDeinitialize(plugin); _gamePlugins.RemoveAt(i); changed = true; } } if (changed) PluginsChanged?.Invoke(); } internal static void Internal_Dispose() { int pluginsCount = _editorPlugins.Count + _gamePlugins.Count; if (pluginsCount == 0) return; Debug.Write(LogType.Info, string.Format("Unloading {0} plugins", pluginsCount)); for (int i = _editorPlugins.Count - 1; i >= 0 && _editorPlugins.Count > 0; i--) { var plugin = _editorPlugins[i]; InvokeDeinitialize(plugin); _editorPlugins.RemoveAt(i); } for (int i = _gamePlugins.Count - 1; i >= 0 && _gamePlugins.Count > 0; i--) { var plugin = _gamePlugins[i]; InvokeDeinitialize(plugin); _gamePlugins.RemoveAt(i); } PluginsChanged?.Invoke(); } } }