From aaa5b0532be35777a5f8b8c823cba35222093f2b Mon Sep 17 00:00:00 2001 From: Andrew Welker Date: Fri, 4 Jul 2025 13:33:56 -0500 Subject: [PATCH] feat: modify plugin loading process --- .../Devices/ConfigSnippetAttribute.cs | 27 ------ .../Devices/DescriptionAttribute.cs | 26 ------ .../Devices/EssentialsDevice.cs | 86 +++++++++++++++++++ .../Devices/EssentialsDeviceFactory.cs | 41 +++++---- ...ssentialsPluginDevelopmentDeviceFactory.cs | 22 ----- .../Devices/EssentialsPluginDeviceFactory.cs | 14 --- .../ProcessorExtensionDeviceFactory.cs | 47 ---------- .../Factory/IDeviceFactory.cs | 4 +- .../ProcessorExtensionDeviceFactory.cs | 2 +- .../Plugins/IPluginDeviceFactory.cs | 18 +--- .../Plugins/PluginLoader.cs | 43 +++++++--- .../DeviceFactory.cs | 2 +- .../MobileControlFactory.cs | 2 +- .../Factory/DeviceFactory.cs | 2 +- 14 files changed, 147 insertions(+), 189 deletions(-) delete mode 100644 src/PepperDash.Essentials.Core/Devices/ConfigSnippetAttribute.cs delete mode 100644 src/PepperDash.Essentials.Core/Devices/DescriptionAttribute.cs delete mode 100644 src/PepperDash.Essentials.Core/Devices/EssentialsPluginDevelopmentDeviceFactory.cs delete mode 100644 src/PepperDash.Essentials.Core/Devices/EssentialsPluginDeviceFactory.cs delete mode 100644 src/PepperDash.Essentials.Core/Devices/ProcessorExtensionDeviceFactory.cs diff --git a/src/PepperDash.Essentials.Core/Devices/ConfigSnippetAttribute.cs b/src/PepperDash.Essentials.Core/Devices/ConfigSnippetAttribute.cs deleted file mode 100644 index a1fdef35..00000000 --- a/src/PepperDash.Essentials.Core/Devices/ConfigSnippetAttribute.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; - -namespace PepperDash.Essentials.Core -{ - /// - /// Represents a ConfigSnippetAttribute - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class ConfigSnippetAttribute : Attribute - { - /// - /// Represents a configuration snippet for the device. - /// - /// - public ConfigSnippetAttribute(string configSnippet) - { - ConfigSnippet = configSnippet; - } - - /// - /// Gets the configuration snippet for the device. - /// This snippet can be used in the DeviceConfig to instantiate the device. - /// - public string ConfigSnippet { get; } - } - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/DescriptionAttribute.cs b/src/PepperDash.Essentials.Core/Devices/DescriptionAttribute.cs deleted file mode 100644 index abe51665..00000000 --- a/src/PepperDash.Essentials.Core/Devices/DescriptionAttribute.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace PepperDash.Essentials.Core -{ - /// - /// Represents a description attribute for a device. - /// - [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] - public class DescriptionAttribute : Attribute - { - /// - /// Represents a description attribute for a device. - /// - /// - public DescriptionAttribute(string description) - { - Description = description; - } - - /// - /// Gets the description for the device. - /// - public string Description { get; } - } - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs index 59fba681..41b46c29 100644 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs +++ b/src/PepperDash.Essentials.Core/Devices/EssentialsDevice.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; using PepperDash.Core; +using PepperDash.Essentials.Core.Config; using Serilog.Events; namespace PepperDash.Essentials.Core @@ -100,4 +102,88 @@ namespace PepperDash.Essentials.Core } } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class DescriptionAttribute : Attribute + { + private string _Description; + + public DescriptionAttribute(string description) + { + //Debug.LogMessage(LogEventLevel.Verbose, "Setting Description: {0}", description); + _Description = description; + } + + public string Description + { + get { return _Description; } + } + } + + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)] + public class ConfigSnippetAttribute : Attribute + { + private string _ConfigSnippet; + + public ConfigSnippetAttribute(string configSnippet) + { + //Debug.LogMessage(LogEventLevel.Verbose, "Setting Config Snippet {0}", configSnippet); + _ConfigSnippet = configSnippet; + } + + public string ConfigSnippet + { + get { return _ConfigSnippet; } + } + } + + /// + /// Represents a factory for creating processor extension devices. + /// + /// The type of the processor extension device. + public abstract class ProcessorExtensionDeviceFactory : IProcessorExtensionDeviceFactory where T : EssentialsDevice + { + #region IProcessorExtensionDeviceFactory Members + + /// + /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device + /// + public List TypeNames { get; protected set; } + + /// + /// Loads an item to the ProcessorExtensionDeviceFactory.ProcessorExtensionFactoryMethods dictionary for each entry in the TypeNames list + /// + public void LoadFactories() + { + foreach (var typeName in TypeNames) + { + //Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + var descriptionAttribute = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; + string description = descriptionAttribute[0].Description; + var snippetAttribute = typeof(T).GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; + ProcessorExtensionDeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice); + } + } + + /// + /// The method that will build the device + /// + /// The device config + /// An instance of the device + public abstract EssentialsDevice BuildDevice(DeviceConfig dc); + + #endregion + + } + + /// + /// Devices the basic needs for a Device Factory + /// + public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice + { + /// + /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") + /// + public string MinimumEssentialsFrameworkVersion { get; protected set; } + } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsDeviceFactory.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsDeviceFactory.cs index 9c1a6a94..bf635c0c 100644 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsDeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Devices/EssentialsDeviceFactory.cs @@ -1,27 +1,32 @@ -using System; +using System; using System.Collections.Generic; using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core { - /// - /// Provides the basic needs for a Device Factory - /// - public abstract class EssentialsDeviceFactory : IDeviceFactory where T : EssentialsDevice - { - /// - public Type FactoryType => typeof(T); - /// - /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device + /// Devices the basic needs for a Device Factory /// - public List TypeNames { get; protected set; } + public abstract class EssentialsDeviceFactory : IDeviceFactory where T:EssentialsDevice + { + #region IDeviceFactory Members + + /// + public Type FactoryType => typeof(T); + + /// + /// A list of strings that can be used in the type property of a DeviceConfig object to build an instance of this device + /// + public List TypeNames { get; protected set; } + + /// + /// The method that will build the device + /// + /// The device config + /// An instance of the device + public abstract EssentialsDevice BuildDevice(DeviceConfig dc); + + #endregion + } - /// - /// Build the device using the configuration - /// - /// The device config - /// An instance of the device - public abstract EssentialsDevice BuildDevice(DeviceConfig dc); - } } \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDevelopmentDeviceFactory.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDevelopmentDeviceFactory.cs deleted file mode 100644 index 8bd60124..00000000 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDevelopmentDeviceFactory.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; - -namespace PepperDash.Essentials.Core -{ - /// - /// EssentialsPluginDevelopmentDeviceFactory class - /// - /// - public abstract class EssentialsPluginDevelopmentDeviceFactory : EssentialsDeviceFactory, IPluginDevelopmentDeviceFactory where T : EssentialsDevice - { - /// - /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") - /// - public string MinimumEssentialsFrameworkVersion { get; protected set; } - - /// - /// Gets or sets the DevelopmentEssentialsFrameworkVersions - /// - public List DevelopmentEssentialsFrameworkVersions { get; protected set; } - } - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDeviceFactory.cs b/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDeviceFactory.cs deleted file mode 100644 index 7a891905..00000000 --- a/src/PepperDash.Essentials.Core/Devices/EssentialsPluginDeviceFactory.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace PepperDash.Essentials.Core -{ - /// - /// Devices the basic needs for a Device Factory - /// - public abstract class EssentialsPluginDeviceFactory : EssentialsDeviceFactory, IPluginDeviceFactory where T : EssentialsDevice - { - /// - /// Specifies the minimum version of Essentials required for a plugin to run. Must use the format Major.Minor.Build (ex. "1.4.33") - /// - public string MinimumEssentialsFrameworkVersion { get; protected set; } - } - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Devices/ProcessorExtensionDeviceFactory.cs b/src/PepperDash.Essentials.Core/Devices/ProcessorExtensionDeviceFactory.cs deleted file mode 100644 index 8549fd38..00000000 --- a/src/PepperDash.Essentials.Core/Devices/ProcessorExtensionDeviceFactory.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Collections.Generic; -using PepperDash.Essentials.Core.Config; - -namespace PepperDash.Essentials.Core -{ - /// - /// Represents a factory for creating processor extension devices. - /// - /// The type of the processor extension device. - [Obsolete("will be removed in a future version")] - public abstract class ProcessorExtensionDeviceFactory : IProcessorExtensionDeviceFactory where T : EssentialsDevice - { - #region IProcessorExtensionDeviceFactory Members - - /// - /// Gets or sets the TypeNames - /// - public List TypeNames { get; protected set; } - - /// - /// LoadFactories method - /// - public void LoadFactories() - { - foreach (var typeName in TypeNames) - { - string description = typeof(T).GetCustomAttributes(typeof(DescriptionAttribute), true) is DescriptionAttribute[] descriptionAttribute && descriptionAttribute.Length > 0 - ? descriptionAttribute[0].Description - : "No description available"; - - ProcessorExtensionDeviceFactory.AddFactoryForType(typeName.ToLower(), description, typeof(T), BuildDevice); - } - } - - /// - /// The method that will build the device - /// - /// The device config - /// An instance of the device - public abstract EssentialsDevice BuildDevice(DeviceConfig dc); - - #endregion - - } - -} \ No newline at end of file diff --git a/src/PepperDash.Essentials.Core/Factory/IDeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/IDeviceFactory.cs index b267edf6..f1a0923e 100644 --- a/src/PepperDash.Essentials.Core/Factory/IDeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Factory/IDeviceFactory.cs @@ -1,6 +1,6 @@ -using System; +using PepperDash.Essentials.Core.Config; +using System; using System.Collections.Generic; -using PepperDash.Essentials.Core.Config; namespace PepperDash.Essentials.Core { diff --git a/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs b/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs index 6336795c..1d9f41e1 100644 --- a/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Factory/ProcessorExtensionDeviceFactory.cs @@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Core /// public ProcessorExtensionDeviceFactory() { var assy = Assembly.GetExecutingAssembly(); - PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy); + PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy); var extensions = assy.GetTypes().Where(ct => typeof(IProcessorExtensionDeviceFactory) .IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract); diff --git a/src/PepperDash.Essentials.Core/Plugins/IPluginDeviceFactory.cs b/src/PepperDash.Essentials.Core/Plugins/IPluginDeviceFactory.cs index 72b6ad7b..caee2dcd 100644 --- a/src/PepperDash.Essentials.Core/Plugins/IPluginDeviceFactory.cs +++ b/src/PepperDash.Essentials.Core/Plugins/IPluginDeviceFactory.cs @@ -1,4 +1,4 @@ -using System; +using PepperDash.Essentials.Core.Config; using System.Collections.Generic; @@ -8,25 +8,11 @@ namespace PepperDash.Essentials.Core /// Defines a class that is capable of loading custom plugin device types /// public interface IPluginDeviceFactory : IDeviceFactory - { + { /// /// Required to define the minimum version for Essentials in the format xx.yy.zz /// string MinimumEssentialsFrameworkVersion { get; } - - } - - /// - /// Defines a factory for creating plugin development devices, including support for specific framework versions. - /// - /// This interface extends to provide additional functionality - /// specific to plugin development environments. - public interface IPluginDevelopmentDeviceFactory : IPluginDeviceFactory - { - /// - /// Gets a list of Essentials versions that this device is compatible with. - /// - List DevelopmentEssentialsFrameworkVersions { get; } } } diff --git a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs index 6a857682..49ebad46 100644 --- a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs +++ b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs @@ -198,7 +198,7 @@ public static class PluginLoader /// this method updates its associated assembly. If no matching name is found, the method does nothing. /// The name used to identify the assembly. This value is case-sensitive and must not be null or empty. /// The assembly to associate with the specified name. This value must not be null. - public static void SetEssentialsAssembly(string name, Assembly assembly) + public static void AddLoadedAssembly(string name, Assembly assembly) { var loadedAssembly = LoadedAssemblies.FirstOrDefault(la => la.Name.Equals(name)); @@ -816,7 +816,7 @@ public static class PluginLoader } } } - + /// /// Loads a custom plugin and performs a dependency check to ensure compatibility with the required Essentials /// framework version. @@ -824,36 +824,53 @@ public static class PluginLoader /// This method verifies that the plugin meets the minimum required Essentials framework version /// before loading it. If the plugin fails the dependency check, it is skipped, and a log message is generated. If /// the plugin passes the check, it is loaded, and its type factories are initialized. - /// The plugin to be loaded, implementing the interface. If the plugin also + /// The plugin to be loaded, implementing the interface. If the plugin also /// implements , additional checks for development versions are /// performed. /// The assembly associated with the plugin being loaded. This is used for logging and tracking purposes. - private static void LoadCustomPlugin(IPluginDeviceFactory plugin, LoadedAssembly loadedAssembly) + private static void LoadCustomPlugin(IPluginDeviceFactory deviceFactory, LoadedAssembly loadedAssembly) { - var passed = plugin is IPluginDevelopmentDeviceFactory developmentPlugin ? Global.IsRunningDevelopmentVersion - (developmentPlugin.DevelopmentEssentialsFrameworkVersions, developmentPlugin.MinimumEssentialsFrameworkVersion) - : Global.IsRunningMinimumVersionOrHigher(plugin.MinimumEssentialsFrameworkVersion); + var passed = Global.IsRunningMinimumVersionOrHigher(deviceFactory.MinimumEssentialsFrameworkVersion); if (!passed) { - Debug.LogMessage(LogEventLevel.Information, - "\r\n********************\r\n\tPlugin indicates minimum Essentials version {0}. Dependency check failed. Skipping Plugin {1}\r\n********************", - plugin.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); + Debug.LogInformation( + "\r\n********************\r\n\tPlugin indicates minimum Essentials version {minimumEssentialsVersion}. Dependency check failed. Skipping Plugin {pluginName}\r\n********************", + deviceFactory.MinimumEssentialsFrameworkVersion, loadedAssembly.Name); return; } else { - Debug.LogMessage(LogEventLevel.Information, "Passed plugin passed dependency check (required version {0})", plugin.MinimumEssentialsFrameworkVersion); + Debug.LogInformation("Passed plugin passed dependency check (required version {essentialsMinimumVersion})", deviceFactory.MinimumEssentialsFrameworkVersion); } - Debug.LogMessage(LogEventLevel.Information, "Loading plugin: {0}", loadedAssembly.Name); - + Debug.LogInformation("Loading plugin: {pluginName}", loadedAssembly.Name); + LoadDeviceFactories(deviceFactory); if (!EssentialsPluginAssemblies.Contains(loadedAssembly)) EssentialsPluginAssemblies.Add(loadedAssembly); } + /// + /// Loads device factories from the specified plugin device factory and registers them for use. + /// + /// This method retrieves metadata from the provided , including + /// type names, descriptions, and configuration snippets, and registers the factory for each device type. The type + /// names are converted to lowercase for registration. + /// The plugin device factory that provides the device types, descriptions, and factory methods to be registered. + private static void LoadDeviceFactories(IPluginDeviceFactory deviceFactory) + { + foreach (var typeName in deviceFactory.TypeNames) + { + //Debug.LogMessage(LogEventLevel.Verbose, "Getting Description Attribute from class: '{0}'", typeof(T).FullName); + var descriptionAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(DescriptionAttribute), true) as DescriptionAttribute[]; + string description = descriptionAttribute[0].Description; + var snippetAttribute = deviceFactory.FactoryType.GetCustomAttributes(typeof(ConfigSnippetAttribute), true) as ConfigSnippetAttribute[]; + DeviceFactory.AddFactoryForType(typeName.ToLower(), description, deviceFactory.FactoryType, deviceFactory.BuildDevice); + } + } + /// /// Loads plugins from the designated plugin directory, processes them, and integrates them into the application. /// diff --git a/src/PepperDash.Essentials.Devices.Common/DeviceFactory.cs b/src/PepperDash.Essentials.Devices.Common/DeviceFactory.cs index 2c7507ba..3a3117cb 100644 --- a/src/PepperDash.Essentials.Devices.Common/DeviceFactory.cs +++ b/src/PepperDash.Essentials.Devices.Common/DeviceFactory.cs @@ -20,7 +20,7 @@ namespace PepperDash.Essentials.Devices.Common public DeviceFactory() { var assy = Assembly.GetExecutingAssembly(); - PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy); + PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy); var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract); diff --git a/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs b/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs index 4f3c5433..9ce584f3 100644 --- a/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs +++ b/src/PepperDash.Essentials.MobileControl/MobileControlFactory.cs @@ -18,7 +18,7 @@ namespace PepperDash.Essentials { var assembly = Assembly.GetExecutingAssembly(); - PluginLoader.SetEssentialsAssembly(assembly.GetName().Name, assembly); + PluginLoader.AddLoadedAssembly(assembly.GetName().Name, assembly); var types = assembly.GetTypes().Where(t => typeof(IDeviceFactory).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract); diff --git a/src/PepperDash.Essentials/Factory/DeviceFactory.cs b/src/PepperDash.Essentials/Factory/DeviceFactory.cs index e60b3fa4..36fca96a 100644 --- a/src/PepperDash.Essentials/Factory/DeviceFactory.cs +++ b/src/PepperDash.Essentials/Factory/DeviceFactory.cs @@ -21,7 +21,7 @@ namespace PepperDash.Essentials public DeviceFactory() { var assy = Assembly.GetExecutingAssembly(); - PluginLoader.SetEssentialsAssembly(assy.GetName().Name, assy); + PluginLoader.AddLoadedAssembly(assy.GetName().Name, assy); var types = assy.GetTypes().Where(ct => typeof(IDeviceFactory).IsAssignableFrom(ct) && !ct.IsInterface && !ct.IsAbstract);