using System; using System.Threading.Tasks; namespace Artemis.Core { /// /// This is the base plugin type, use the other interfaces such as Module to create plugins /// public abstract class Plugin : IDisposable { /// /// Gets the plugin info related to this plugin /// public PluginInfo PluginInfo { get; internal set; } /// /// Gets whether the plugin is enabled /// public bool Enabled { get; private set; } /// /// Gets or sets a configuration dialog for this plugin that is accessible in the UI under Settings > Plugins /// public PluginConfigurationDialog ConfigurationDialog { get; protected set; } /// public void Dispose() { DisablePlugin(); } /// /// Called when the plugin is activated /// public abstract void EnablePlugin(); /// /// Called when the plugin is deactivated or when Artemis shuts down /// public abstract void DisablePlugin(); internal void SetEnabled(bool enable, bool isAutoEnable = false) { if (enable && !Enabled) { try { if (isAutoEnable && PluginInfo.GetLockFileCreated()) { // Don't wrap existing lock exceptions, simply rethrow them if (PluginInfo.LoadException is ArtemisPluginLockException) throw PluginInfo.LoadException; throw new ArtemisPluginLockException(PluginInfo.LoadException); } Enabled = true; PluginInfo.Enabled = true; PluginInfo.CreateLockFile(); // Allow up to 15 seconds for plugins to activate. // This means plugins that need more time should do their long running tasks in a background thread, which is intentional // Little meh: Running this from a different thread could cause deadlocks var enableTask = Task.Run(InternalEnablePlugin); if (!enableTask.Wait(TimeSpan.FromSeconds(15))) throw new ArtemisPluginException(PluginInfo, "Plugin load timeout"); PluginInfo.LoadException = null; OnPluginEnabled(); } // If enable failed, put it back in a disabled state catch (Exception e) { Enabled = false; PluginInfo.Enabled = false; PluginInfo.LoadException = e; throw; } finally { if (!(PluginInfo.LoadException is ArtemisPluginLockException)) PluginInfo.DeleteLockFile(); } } else if (!enable && Enabled) { Enabled = false; PluginInfo.Enabled = false; // Even if disable failed, still leave it in a disabled state to avoid more issues InternalDisablePlugin(); OnPluginDisabled(); } } internal virtual void InternalEnablePlugin() { EnablePlugin(); } internal virtual void InternalDisablePlugin() { DisablePlugin(); } #region Events /// /// Occurs when the plugin is enabled /// public event EventHandler PluginEnabled; /// /// Occurs when the plugin is disabled /// public event EventHandler PluginDisabled; /// /// Triggers the PluginEnabled event /// protected virtual void OnPluginEnabled() { PluginEnabled?.Invoke(this, EventArgs.Empty); } /// /// Triggers the PluginDisabled event /// protected virtual void OnPluginDisabled() { PluginDisabled?.Invoke(this, EventArgs.Empty); } #endregion } }