diff --git a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs
index 2f0839da..9211080c 100644
--- a/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs
+++ b/src/PepperDash.Essentials.Core/Plugins/PluginLoader.cs
@@ -1,193 +1,192 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Crestron.SimplSharp;
-using System.Reflection;
-using System.IO;
-using System.Reflection.PortableExecutable;
-using System.Reflection.Metadata;
-
-using PepperDash.Core;
-using PepperDash.Essentials.Core;
-using Serilog.Events;
-
-
-namespace PepperDash.Essentials;
-
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Crestron.SimplSharp;
+using System.Reflection;
+using System.IO;
+using System.Reflection.PortableExecutable;
+using System.Reflection.Metadata;
+
+using PepperDash.Core;
+using PepperDash.Essentials.Core;
+using Serilog.Events;
+
+
+namespace PepperDash.Essentials;
///
/// Provides functionality for loading and managing plugins and assemblies in the application.
-///
-/// The class is responsible for discovering, loading, and managing assemblies
-/// and plugins, including handling compatibility checks for .NET 8. It supports loading assemblies from the program
-/// directory, plugins folder, and .cplz archives. Additionally, it tracks incompatible plugins and provides reporting
-/// capabilities for loaded assemblies and their versions.
-public static class PluginLoader
-{
+///
+/// The class is responsible for discovering, loading, and managing assemblies
+/// and plugins, including handling compatibility checks for .NET 8. It supports loading assemblies from the program
+/// directory, plugins folder, and .cplz archives. Additionally, it tracks incompatible plugins and provides reporting
+/// capabilities for loaded assemblies and their versions.
+public static class PluginLoader
+{
///
/// Gets the list of assemblies that have been loaded into the application.
- ///
- public static List LoadedAssemblies { get; private set; }
-
+ ///
+ public static List LoadedAssemblies { get; private set; }
+
///
/// Represents a collection of assemblies loaded from the plugin folder.
- ///
- /// This field is used to store assemblies that have been dynamically loaded from a designated
- /// plugin folder. It is intended for internal use and should not be modified directly.
- private static readonly List LoadedPluginFolderAssemblies;
-
+ ///
+ /// This field is used to store assemblies that have been dynamically loaded from a designated
+ /// plugin folder. It is intended for internal use and should not be modified directly.
+ private static readonly List LoadedPluginFolderAssemblies;
+
///
/// Gets the list of plugins that are incompatible with the current system or configuration.
- ///
- /// This property provides information about plugins that are not supported or cannot function
- /// correctly in the current environment. Use this list to identify and handle incompatible plugins appropriately in
- /// your application logic.
- public static List IncompatiblePlugins { get; private set; }
-
+ ///
+ /// This property provides information about plugins that are not supported or cannot function
+ /// correctly in the current environment. Use this list to identify and handle incompatible plugins appropriately in
+ /// your application logic.
+ public static List IncompatiblePlugins { get; private set; }
+
///
/// Gets the loaded assembly that contains the core functionality of the application.
- ///
- public static LoadedAssembly EssentialsAssembly { get; private set; }
-
+ ///
+ public static LoadedAssembly EssentialsAssembly { get; private set; }
+
///
/// Gets the loaded assembly information for the PepperDash Core library.
- ///
- public static LoadedAssembly PepperDashCoreAssembly { get; private set; }
-
+ ///
+ public static LoadedAssembly PepperDashCoreAssembly { get; private set; }
+
///
/// Gets the list of assemblies that are Essentials plugins loaded by the application.
- ///
- public static List EssentialsPluginAssemblies { get; private set; }
-
+ ///
+ public static List EssentialsPluginAssemblies { get; private set; }
+
///
/// Gets the directory path where plugins are stored.
- ///
- private static string PluginDirectory => Global.FilePathPrefix + "plugins";
-
+ ///
+ private static string PluginDirectory => Global.FilePathPrefix + "plugins";
+
///
/// Gets the directory path where loaded plugin assemblies are stored.
- ///
- private static string LoadedPluginsDirectoryPath => PluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
-
+ ///
+ private static string LoadedPluginsDirectoryPath => PluginDirectory + Global.DirectorySeparator + "loadedAssemblies";
+
///
/// Gets the path to the temporary directory used by the plugin.
- ///
- private static string TempDirectory => PluginDirectory + Global.DirectorySeparator + "temp";
-
+ ///
+ private static string TempDirectory => PluginDirectory + Global.DirectorySeparator + "temp";
+
///
/// Represents a collection of fully qualified type names that are known to be incompatible with the current
/// application or framework.
- ///
- /// This collection contains the names of types that are deprecated, obsolete, or otherwise
- /// incompatible with the intended usage of the application. These types may represent security risks, unsupported
- /// features, or legacy APIs that should be avoided.
+ ///
+ /// This collection contains the names of types that are deprecated, obsolete, or otherwise
+ /// incompatible with the intended usage of the application. These types may represent security risks, unsupported
+ /// features, or legacy APIs that should be avoided.
private static readonly HashSet KnownIncompatibleTypes =
- [
- "System.Net.ICertificatePolicy",
- "System.Security.Cryptography.SHA1CryptoServiceProvider",
- "System.Web.HttpUtility",
- "System.Configuration.ConfigurationManager",
- "System.Web.Services.Protocols.SoapHttpClientProtocol",
- "System.Runtime.Remoting",
- "System.EnterpriseServices",
- "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter",
- "System.Security.SecurityManager",
- "System.Security.Permissions.FileIOPermission",
- "System.AppDomain.CreateDomain"
- ];
-
+ [
+ "System.Net.ICertificatePolicy",
+ "System.Security.Cryptography.SHA1CryptoServiceProvider",
+ "System.Web.HttpUtility",
+ "System.Configuration.ConfigurationManager",
+ "System.Web.Services.Protocols.SoapHttpClientProtocol",
+ "System.Runtime.Remoting",
+ "System.EnterpriseServices",
+ "System.Runtime.Serialization.Formatters.Binary.BinaryFormatter",
+ "System.Security.SecurityManager",
+ "System.Security.Permissions.FileIOPermission",
+ "System.AppDomain.CreateDomain"
+ ];
+
///
/// Initializes static members of the class.
- ///
- /// This static constructor initializes the collections used to manage plugin assemblies and
- /// track incompatible plugins.
- static PluginLoader()
- {
- LoadedAssemblies = [];
- LoadedPluginFolderAssemblies = [];
- EssentialsPluginAssemblies = [];
- IncompatiblePlugins = [];
- }
-
+ ///
+ /// This static constructor initializes the collections used to manage plugin assemblies and
+ /// track incompatible plugins.
+ static PluginLoader()
+ {
+ LoadedAssemblies = [];
+ LoadedPluginFolderAssemblies = [];
+ EssentialsPluginAssemblies = [];
+ IncompatiblePlugins = [];
+ }
+
///
/// Loads and registers assemblies from the application's directory that match specific naming patterns.
- ///
- /// This method scans the application's directory for assemblies with filenames containing
- /// "Essentials" or "PepperDash" and registers them in the collection. It also
- /// assigns specific assemblies to predefined properties, such as and , based on their names. Debug messages are logged at various stages to provide
- /// detailed information about the process, including the number of assemblies found and their versions. This
- /// method is intended to be used during application initialization to ensure required assemblies are loaded and
- /// tracked.
- public static void AddProgramAssemblies()
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Getting Assemblies loaded with Essentials");
- // Get the loaded assembly filenames
- var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix);
- var assemblyFiles = appDi.GetFiles("*.dll");
-
- Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length);
-
- foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash")))
- {
- string version = string.Empty;
- Assembly assembly = null;
-
- switch (fi.Name)
- {
- case ("PepperDashEssentials.dll"):
- {
- version = Global.AssemblyVersion;
- EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);
- break;
- }
- case ("PepperDash_Essentials_Core.dll"):
- {
- version = Global.AssemblyVersion;
- break;
- }
- case ("Essentials Devices Common.dll"):
- {
- version = Global.AssemblyVersion;
- break;
- }
- case ("PepperDashCore.dll"):
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll");
- version = Debug.PepperDashCoreVersion;
- Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
- PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
- break;
- }
- }
-
- LoadedAssemblies.Add(new LoadedAssembly(fi.Name, version, assembly));
- }
-
- if (Debug.Level > 1)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Loaded Assemblies:");
-
- foreach (var assembly in LoadedAssemblies)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Assembly: {0}", assembly.Name);
- }
- }
- }
-
+ ///
+ /// This method scans the application's directory for assemblies with filenames containing
+ /// "Essentials" or "PepperDash" and registers them in the collection. It also
+ /// assigns specific assemblies to predefined properties, such as and , based on their names. Debug messages are logged at various stages to provide
+ /// detailed information about the process, including the number of assemblies found and their versions. This
+ /// method is intended to be used during application initialization to ensure required assemblies are loaded and
+ /// tracked.
+ public static void AddProgramAssemblies()
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Getting Assemblies loaded with Essentials");
+ // Get the loaded assembly filenames
+ var appDi = new DirectoryInfo(Global.ApplicationDirectoryPathPrefix);
+ var assemblyFiles = appDi.GetFiles("*.dll");
+
+ Debug.LogMessage(LogEventLevel.Verbose, "Found {0} Assemblies", assemblyFiles.Length);
+
+ foreach (var fi in assemblyFiles.Where(fi => fi.Name.Contains("Essentials") || fi.Name.Contains("PepperDash")))
+ {
+ string version = string.Empty;
+ Assembly assembly = null;
+
+ switch (fi.Name)
+ {
+ case ("PepperDashEssentials.dll"):
+ {
+ version = Global.AssemblyVersion;
+ EssentialsAssembly = new LoadedAssembly(fi.Name, version, assembly);
+ break;
+ }
+ case ("PepperDash_Essentials_Core.dll"):
+ {
+ version = Global.AssemblyVersion;
+ break;
+ }
+ case ("Essentials Devices Common.dll"):
+ {
+ version = Global.AssemblyVersion;
+ break;
+ }
+ case ("PepperDashCore.dll"):
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Found PepperDash_Core.dll");
+ version = Debug.PepperDashCoreVersion;
+ Debug.LogMessage(LogEventLevel.Verbose, "PepperDash_Core Version: {0}", version);
+ PepperDashCoreAssembly = new LoadedAssembly(fi.Name, version, assembly);
+ break;
+ }
+ }
+
+ LoadedAssemblies.Add(new LoadedAssembly(fi.Name, version, assembly));
+ }
+
+ if (Debug.Level > 1)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Loaded Assemblies:");
+
+ foreach (var assembly in LoadedAssemblies)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Assembly: {0}", assembly.Name);
+ }
+ }
+ }
+
///
/// Associates the specified assembly with the given name in the loaded assemblies collection.
///
/// If an assembly with the specified name already exists in the loaded assemblies collection,
/// 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 AddLoadedAssembly(string name, Assembly assembly)
- {
- var loadedAssembly = LoadedAssemblies.FirstOrDefault(la => la.Name.Equals(name));
-
- loadedAssembly?.SetAssembly(assembly);
- }
-
+ /// The assembly to associate with the specified name. This value must not be null.
+ public static void AddLoadedAssembly(string name, Assembly assembly)
+ {
+ var loadedAssembly = LoadedAssemblies.FirstOrDefault(la => la.Name.Equals(name));
+
+ loadedAssembly?.SetAssembly(assembly);
+ }
+
///
/// Determines whether a plugin assembly is compatible with .NET 8.
///
@@ -195,19 +194,19 @@ public static class PluginLoader
/// for known incompatible types, inspecting custom attributes, and collecting assembly references. If the analysis
/// encounters an error, the method returns with an appropriate error message.
/// The file path to the plugin assembly to analyze.
- /// A tuple containing the following: - if the plugin
- /// is compatible with .NET 8; otherwise, .
- A
- /// string providing the reason for incompatibility, or if the plugin is
- /// compatible.
- A list of assembly references found in the
- /// plugin.
- public static (bool IsCompatible, string Reason, List References) IsPluginCompatibleWithNet8(string filePath)
- {
- try
- {
+ /// A tuple containing the following: - if the plugin
+ /// is compatible with .NET 8; otherwise, .
- A
+ /// string providing the reason for incompatibility, or if the plugin is
+ /// compatible.
- A list of assembly references found in the
+ /// plugin.
+ public static (bool IsCompatible, string Reason, List References) IsPluginCompatibleWithNet8(string filePath)
+ {
+ try
+ {
List referencedAssemblies = [];
using FileStream fs = new(filePath, FileMode.Open,
- FileAccess.Read, FileShare.ReadWrite);
+ FileAccess.Read, FileShare.ReadWrite);
using PEReader peReader = new(fs);
if (!peReader.HasMetadata)
@@ -264,14 +263,14 @@ public static class PluginLoader
}
// If we can't determine incompatibility, assume it's compatible
- return (true, null, referencedAssemblies);
- }
- catch (Exception ex)
- {
- return (false, $"Error analyzing assembly: {ex.Message}", new List());
- }
- }
-
+ return (true, null, referencedAssemblies);
+ }
+ catch (Exception ex)
+ {
+ return (false, $"Error analyzing assembly: {ex.Message}", new List());
+ }
+ }
+
///
/// Loads an assembly from the specified file path and verifies its compatibility with .NET 8.
///
@@ -285,80 +284,80 @@ public static class PluginLoader
/// compatibility issues.
/// The full path to the assembly file to load. This cannot be null or empty.
/// An optional identifier for the entity requesting the load operation. This can be null.
- /// A object representing the loaded assembly if the operation succeeds; otherwise,
- /// .
- private static LoadedAssembly LoadAssembly(string filePath, string requestedBy = null)
- {
- try
- {
- // Check .NET 8 compatibility before loading
- var (isCompatible, reason, references) = IsPluginCompatibleWithNet8(filePath);
- if (!isCompatible)
- {
- string fileName = Path.GetFileName(filePath);
- Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
-
- var incompatiblePlugin = new IncompatiblePlugin(fileName, reason, requestedBy);
- IncompatiblePlugins.Add(incompatiblePlugin);
- return null;
- }
-
- var assembly = Assembly.LoadFrom(filePath);
- if (assembly != null)
- {
- var assyVersion = GetAssemblyVersion(assembly);
- var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
- LoadedAssemblies.Add(loadedAssembly);
- Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
- return loadedAssembly;
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
- }
- return null;
- }
- catch(FileLoadException ex) when (ex.Message.Contains("Assembly with same name is already loaded"))
- {
- // Get the assembly name from the file path
- string assemblyName = Path.GetFileNameWithoutExtension(filePath);
-
- // Try to find the already loaded assembly
- var existingAssembly = AppDomain.CurrentDomain.GetAssemblies()
- .FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.OrdinalIgnoreCase));
-
- if (existingAssembly != null)
- {
- Debug.LogMessage(LogEventLevel.Information, "Assembly '{0}' is already loaded, using existing instance", assemblyName);
- var assyVersion = GetAssemblyVersion(existingAssembly);
- var loadedAssembly = new LoadedAssembly(existingAssembly.GetName().Name, assyVersion, existingAssembly);
- LoadedAssemblies.Add(loadedAssembly);
- return loadedAssembly;
- }
-
- Debug.LogMessage(LogEventLevel.Warning, "Assembly with same name already loaded but couldn't find it: {0}", filePath);
- return null;
- }
- catch(Exception ex)
- {
- string fileName = Path.GetFileName(filePath);
-
- // Check if this might be a .NET Framework compatibility issue
- if (ex.Message.Contains("Could not load type") ||
- ex.Message.Contains("Unable to load one or more of the requested types"))
- {
- Debug.LogMessage(LogEventLevel.Error, "Error loading assembly {0}: Likely .NET 8 compatibility issue: {1}",
- fileName, ex.Message);
- IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, ex.Message, requestedBy));
- }
- else
- {
- Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
- }
- return null;
- }
- }
-
+ /// A object representing the loaded assembly if the operation succeeds; otherwise,
+ /// .
+ private static LoadedAssembly LoadAssembly(string filePath, string requestedBy = null)
+ {
+ try
+ {
+ // Check .NET 8 compatibility before loading
+ var (isCompatible, reason, references) = IsPluginCompatibleWithNet8(filePath);
+ if (!isCompatible)
+ {
+ string fileName = Path.GetFileName(filePath);
+ Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
+
+ var incompatiblePlugin = new IncompatiblePlugin(fileName, reason, requestedBy);
+ IncompatiblePlugins.Add(incompatiblePlugin);
+ return null;
+ }
+
+ var assembly = Assembly.LoadFrom(filePath);
+ if (assembly != null)
+ {
+ var assyVersion = GetAssemblyVersion(assembly);
+ var loadedAssembly = new LoadedAssembly(assembly.GetName().Name, assyVersion, assembly);
+ LoadedAssemblies.Add(loadedAssembly);
+ Debug.LogMessage(LogEventLevel.Information, "Loaded assembly '{0}', version {1}", loadedAssembly.Name, loadedAssembly.Version);
+ return loadedAssembly;
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Unable to load assembly: '{0}'", filePath);
+ }
+ return null;
+ }
+ catch(FileLoadException ex) when (ex.Message.Contains("Assembly with same name is already loaded"))
+ {
+ // Get the assembly name from the file path
+ string assemblyName = Path.GetFileNameWithoutExtension(filePath);
+
+ // Try to find the already loaded assembly
+ var existingAssembly = AppDomain.CurrentDomain.GetAssemblies()
+ .FirstOrDefault(a => a.GetName().Name.Equals(assemblyName, StringComparison.OrdinalIgnoreCase));
+
+ if (existingAssembly != null)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Assembly '{0}' is already loaded, using existing instance", assemblyName);
+ var assyVersion = GetAssemblyVersion(existingAssembly);
+ var loadedAssembly = new LoadedAssembly(existingAssembly.GetName().Name, assyVersion, existingAssembly);
+ LoadedAssemblies.Add(loadedAssembly);
+ return loadedAssembly;
+ }
+
+ Debug.LogMessage(LogEventLevel.Warning, "Assembly with same name already loaded but couldn't find it: {0}", filePath);
+ return null;
+ }
+ catch(Exception ex)
+ {
+ string fileName = Path.GetFileName(filePath);
+
+ // Check if this might be a .NET Framework compatibility issue
+ if (ex.Message.Contains("Could not load type") ||
+ ex.Message.Contains("Unable to load one or more of the requested types"))
+ {
+ Debug.LogMessage(LogEventLevel.Error, "Error loading assembly {0}: Likely .NET 8 compatibility issue: {1}",
+ fileName, ex.Message);
+ IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, ex.Message, requestedBy));
+ }
+ else
+ {
+ Debug.LogMessage(ex, "Error loading assembly from {path}", null, filePath);
+ }
+ return null;
+ }
+ }
+
///
/// Retrieves the version information of the specified assembly.
///
@@ -366,52 +365,52 @@ public static class PluginLoader
/// cref="AssemblyInformationalVersionAttribute"/>. If the attribute is not present, it falls back to the assembly's
/// version as defined in its metadata.
/// The assembly from which to retrieve the version information. Cannot be .
- /// A string representing the version of the assembly. If the assembly has an , its value is returned. Otherwise, the assembly's
- /// version is returned in the format "Major.Minor.Build.Revision".
- public static string GetAssemblyVersion(Assembly assembly)
- {
- var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
- if (ver != null && ver.Length > 0)
- {
- // Get the AssemblyInformationalVersion
- AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
- return verAttribute.InformationalVersion;
- }
- else
- {
- // Get the AssemblyVersion
- var version = assembly.GetName().Version;
- var verStr = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
- return verStr;
- }
- }
-
+ /// A string representing the version of the assembly. If the assembly has an , its value is returned. Otherwise, the assembly's
+ /// version is returned in the format "Major.Minor.Build.Revision".
+ public static string GetAssemblyVersion(Assembly assembly)
+ {
+ var ver = assembly.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
+ if (ver != null && ver.Length > 0)
+ {
+ // Get the AssemblyInformationalVersion
+ AssemblyInformationalVersionAttribute verAttribute = ver[0] as AssemblyInformationalVersionAttribute;
+ return verAttribute.InformationalVersion;
+ }
+ else
+ {
+ // Get the AssemblyVersion
+ var version = assembly.GetName().Version;
+ var verStr = string.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
+ return verStr;
+ }
+ }
+
///
/// Determines whether an assembly with the specified name is currently loaded.
///
/// This method performs a case-sensitive comparison to determine if the specified assembly is
/// loaded. It logs verbose messages indicating the status of the check.
/// The name of the assembly to check. This value is case-sensitive.
- /// if an assembly with the specified name is loaded; otherwise, .
- public static bool CheckIfAssemblyLoaded(string name)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Checking if assembly: {0} is loaded...", name);
- var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.Name.Equals(name));
-
- if (loadedAssembly != null)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Assembly already loaded.");
- return true;
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Assembly not loaded.");
- return false;
- }
- }
-
+ /// if an assembly with the specified name is loaded; otherwise, .
+ public static bool CheckIfAssemblyLoaded(string name)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Checking if assembly: {0} is loaded...", name);
+ var loadedAssembly = LoadedAssemblies.FirstOrDefault(s => s.Name.Equals(name));
+
+ if (loadedAssembly != null)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Assembly already loaded.");
+ return true;
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Assembly not loaded.");
+ return false;
+ }
+ }
+
///
/// Reports the versions of the Essentials framework, PepperDash Core, and loaded plugins to the console.
///
@@ -419,304 +418,304 @@ public static class PluginLoader
/// all loaded Essentials plugins to the Crestron console. If any incompatible plugins are detected, their details
/// are also reported, including the reason for incompatibility and the plugin that required them, if
/// applicable.
- /// The command string that triggered the version report. This parameter is not used directly by the method.
- public static void ReportAssemblyVersions(string command)
- {
- CrestronConsole.ConsoleCommandResponse("Essentials Version: {0}" + CrestronEnvironment.NewLine, Global.AssemblyVersion);
- CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);
- CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine);
- foreach (var assembly in EssentialsPluginAssemblies)
- {
- CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
- }
-
- if (IncompatiblePlugins.Count > 0)
- {
- CrestronConsole.ConsoleCommandResponse("Incompatible Plugins:" + CrestronEnvironment.NewLine);
- foreach (var plugin in IncompatiblePlugins)
- {
- if (plugin.TriggeredBy != "Direct load")
- {
- CrestronConsole.ConsoleCommandResponse("{0}: {1} (Required by: {2})" + CrestronEnvironment.NewLine,
- plugin.Name, plugin.Reason, plugin.TriggeredBy);
- }
- else
- {
- CrestronConsole.ConsoleCommandResponse("{0}: {1}" + CrestronEnvironment.NewLine,
- plugin.Name, plugin.Reason);
- }
- }
- }
+ /// The command string that triggered the version report. This parameter is not used directly by the method.
+ public static void ReportAssemblyVersions(string command)
+ {
+ CrestronConsole.ConsoleCommandResponse("Essentials Version: {0}" + CrestronEnvironment.NewLine, Global.AssemblyVersion);
+ CrestronConsole.ConsoleCommandResponse("PepperDash Core Version: {0}" + CrestronEnvironment.NewLine, PepperDashCoreAssembly.Version);
+ CrestronConsole.ConsoleCommandResponse("Essentials Plugin Versions:" + CrestronEnvironment.NewLine);
+ foreach (var assembly in EssentialsPluginAssemblies)
+ {
+ CrestronConsole.ConsoleCommandResponse("{0} Version: {1}" + CrestronEnvironment.NewLine, assembly.Name, assembly.Version);
+ }
+
+ if (IncompatiblePlugins.Count > 0)
+ {
+ CrestronConsole.ConsoleCommandResponse("Incompatible Plugins:" + CrestronEnvironment.NewLine);
+ foreach (var plugin in IncompatiblePlugins)
+ {
+ if (plugin.TriggeredBy != "Direct load")
+ {
+ CrestronConsole.ConsoleCommandResponse("{0}: {1} (Required by: {2})" + CrestronEnvironment.NewLine,
+ plugin.Name, plugin.Reason, plugin.TriggeredBy);
+ }
+ else
+ {
+ CrestronConsole.ConsoleCommandResponse("{0}: {1}" + CrestronEnvironment.NewLine,
+ plugin.Name, plugin.Reason);
+ }
+ }
+ }
}
///
/// Moves .dll assemblies from the plugins folder to the loaded plugins directory.
- ///
- /// This method scans the plugins folder for .dll files and moves them to the loaded plugins
- /// directory if they are not already loaded. If a file with the same name exists in the target directory, it is
- /// replaced. The method logs the process at various stages and handles exceptions for individual files to ensure
- /// the operation continues for other files.
- private static void MoveDllAssemblies()
- {
- Debug.LogMessage(LogEventLevel.Information, "Looking for .dll assemblies from plugins folder...");
-
- var pluginDi = new DirectoryInfo(PluginDirectory);
- var pluginFiles = pluginDi.GetFiles("*.dll");
-
- if (pluginFiles.Length > 0)
- {
- if (!Directory.Exists(LoadedPluginsDirectoryPath))
- {
- Directory.CreateDirectory(LoadedPluginsDirectoryPath);
- }
- }
-
- foreach (var pluginFile in pluginFiles)
- {
- try
- {
- Debug.LogMessage(LogEventLevel.Information, "Found .dll: {0}", pluginFile.Name);
-
- if (!CheckIfAssemblyLoaded(pluginFile.Name))
- {
- string filePath = string.Empty;
-
- filePath = LoadedPluginsDirectoryPath + Global.DirectorySeparator + pluginFile.Name;
-
- // Check if there is a previous file in the loadedPlugins directory and delete
- if (File.Exists(filePath))
- {
- Debug.LogMessage(LogEventLevel.Information, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
- File.Delete(filePath);
- }
-
- // Move the file
- File.Move(pluginFile.FullName, filePath);
- Debug.LogMessage(LogEventLevel.Verbose, "Moved {0} to {1}", pluginFile.FullName, filePath);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Skipping assembly: {0}. There is already an assembly with that name loaded.", pluginFile.FullName);
- }
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Error with plugin file {0} . Exception: {1}", pluginFile.FullName, e);
- continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
- }
- }
-
- Debug.LogMessage(LogEventLevel.Information, "Done with .dll assemblies");
- }
-
+ ///
+ /// This method scans the plugins folder for .dll files and moves them to the loaded plugins
+ /// directory if they are not already loaded. If a file with the same name exists in the target directory, it is
+ /// replaced. The method logs the process at various stages and handles exceptions for individual files to ensure
+ /// the operation continues for other files.
+ private static void MoveDllAssemblies()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Looking for .dll assemblies from plugins folder...");
+
+ var pluginDi = new DirectoryInfo(PluginDirectory);
+ var pluginFiles = pluginDi.GetFiles("*.dll");
+
+ if (pluginFiles.Length > 0)
+ {
+ if (!Directory.Exists(LoadedPluginsDirectoryPath))
+ {
+ Directory.CreateDirectory(LoadedPluginsDirectoryPath);
+ }
+ }
+
+ foreach (var pluginFile in pluginFiles)
+ {
+ try
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Found .dll: {0}", pluginFile.Name);
+
+ if (!CheckIfAssemblyLoaded(pluginFile.Name))
+ {
+ string filePath = string.Empty;
+
+ filePath = LoadedPluginsDirectoryPath + Global.DirectorySeparator + pluginFile.Name;
+
+ // Check if there is a previous file in the loadedPlugins directory and delete
+ if (File.Exists(filePath))
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
+ File.Delete(filePath);
+ }
+
+ // Move the file
+ File.Move(pluginFile.FullName, filePath);
+ Debug.LogMessage(LogEventLevel.Verbose, "Moved {0} to {1}", pluginFile.FullName, filePath);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Skipping assembly: {0}. There is already an assembly with that name loaded.", pluginFile.FullName);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Error with plugin file {0} . Exception: {1}", pluginFile.FullName, e);
+ continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
+ }
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "Done with .dll assemblies");
+ }
+
///
/// Extracts and processes .cplz archive files found in the specified directories, moving their contents to the
/// appropriate plugin directory.
- ///
- /// This method searches for .cplz files in the plugin directory and user folder, extracts their
- /// contents, and moves any .dll files to the loaded plugins directory. If a .dll file with the same name already
- /// exists in the target directory, it is replaced with the new file. Temporary files and directories created during
- /// the process are cleaned up after the operation completes.
- private static void UnzipAndMoveCplzArchives()
- {
- Debug.LogMessage(LogEventLevel.Information, "Looking for .cplz archives from plugins folder...");
- var di = new DirectoryInfo(PluginDirectory);
- var zFiles = di.GetFiles("*.cplz");
-
- //// Find cplz files at the root of the user folder. Makes development/testing easier for VC-4, and helps with mistakes by end users
-
- //var userDi = new DirectoryInfo(Global.FilePathPrefix);
- //var userZFiles = userDi.GetFiles("*.cplz");
-
- Debug.LogInformation("Checking {folder} for .cplz files", Global.FilePathPrefix);
- var cplzFiles = Directory.GetFiles(Global.FilePathPrefix, "*.cplz", SearchOption.AllDirectories)
- .Select(f => new FileInfo(f))
- .ToArray();
-
- if (cplzFiles.Length > 0)
- {
- if (!Directory.Exists(LoadedPluginsDirectoryPath))
- {
- Directory.CreateDirectory(LoadedPluginsDirectoryPath);
- }
- }
-
- foreach (var zfi in cplzFiles)
- {
- Directory.CreateDirectory(TempDirectory);
- var tempDi = new DirectoryInfo(TempDirectory);
-
- Debug.LogMessage(LogEventLevel.Information, "Found cplz: {0}. Unzipping into temp plugins directory", zfi.FullName);
- var result = CrestronZIP.Unzip(zfi.FullName, tempDi.FullName);
- Debug.LogMessage(LogEventLevel.Information, "UnZip Result: {0}", result.ToString());
-
- var tempFiles = tempDi.GetFiles("*.dll");
- foreach (var tempFile in tempFiles)
- {
- try
- {
- if (!CheckIfAssemblyLoaded(tempFile.Name))
- {
- string filePath = string.Empty;
-
- filePath = LoadedPluginsDirectoryPath + Global.DirectorySeparator + tempFile.Name;
-
- // Check if there is a previous file in the loadedPlugins directory and delete
- if (File.Exists(filePath))
- {
- Debug.LogMessage(LogEventLevel.Information, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
- File.Delete(filePath);
- }
-
- // Move the file
- File.Move(tempFile.FullName, filePath);
- Debug.LogMessage(LogEventLevel.Verbose, "Moved {0} to {1}", tempFile.FullName, filePath);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Information, "Skipping assembly: {0}. There is already an assembly with that name loaded.", tempFile.FullName);
- }
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Verbose, "Assembly {0} is not a custom assembly. Exception: {1}", tempFile.FullName, e);
- continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
- }
- }
-
- // Delete the .cplz and the temp directory
- Directory.Delete(TempDirectory, true);
- zfi.Delete();
- }
-
- Debug.LogMessage(LogEventLevel.Information, "Done with .cplz archives");
- }
-
+ ///
+ /// This method searches for .cplz files in the plugin directory and user folder, extracts their
+ /// contents, and moves any .dll files to the loaded plugins directory. If a .dll file with the same name already
+ /// exists in the target directory, it is replaced with the new file. Temporary files and directories created during
+ /// the process are cleaned up after the operation completes.
+ private static void UnzipAndMoveCplzArchives()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Looking for .cplz archives from plugins folder...");
+ var di = new DirectoryInfo(PluginDirectory);
+ var zFiles = di.GetFiles("*.cplz");
+
+ //// Find cplz files at the root of the user folder. Makes development/testing easier for VC-4, and helps with mistakes by end users
+
+ //var userDi = new DirectoryInfo(Global.FilePathPrefix);
+ //var userZFiles = userDi.GetFiles("*.cplz");
+
+ Debug.LogInformation("Checking {folder} for .cplz files", Global.FilePathPrefix);
+ var cplzFiles = Directory.GetFiles(Global.FilePathPrefix, "*.cplz", SearchOption.AllDirectories)
+ .Select(f => new FileInfo(f))
+ .ToArray();
+
+ if (cplzFiles.Length > 0)
+ {
+ if (!Directory.Exists(LoadedPluginsDirectoryPath))
+ {
+ Directory.CreateDirectory(LoadedPluginsDirectoryPath);
+ }
+ }
+
+ foreach (var zfi in cplzFiles)
+ {
+ Directory.CreateDirectory(TempDirectory);
+ var tempDi = new DirectoryInfo(TempDirectory);
+
+ Debug.LogMessage(LogEventLevel.Information, "Found cplz: {0}. Unzipping into temp plugins directory", zfi.FullName);
+ var result = CrestronZIP.Unzip(zfi.FullName, tempDi.FullName);
+ Debug.LogMessage(LogEventLevel.Information, "UnZip Result: {0}", result.ToString());
+
+ var tempFiles = tempDi.GetFiles("*.dll");
+ foreach (var tempFile in tempFiles)
+ {
+ try
+ {
+ if (!CheckIfAssemblyLoaded(tempFile.Name))
+ {
+ string filePath = string.Empty;
+
+ filePath = LoadedPluginsDirectoryPath + Global.DirectorySeparator + tempFile.Name;
+
+ // Check if there is a previous file in the loadedPlugins directory and delete
+ if (File.Exists(filePath))
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Found existing file in loadedPlugins: {0} Deleting and moving new file to replace it", filePath);
+ File.Delete(filePath);
+ }
+
+ // Move the file
+ File.Move(tempFile.FullName, filePath);
+ Debug.LogMessage(LogEventLevel.Verbose, "Moved {0} to {1}", tempFile.FullName, filePath);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Skipping assembly: {0}. There is already an assembly with that name loaded.", tempFile.FullName);
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Verbose, "Assembly {0} is not a custom assembly. Exception: {1}", tempFile.FullName, e);
+ continue; //catching any load issues and continuing. There will be exceptions loading Crestron .dlls from the cplz Probably should do something different here
+ }
+ }
+
+ // Delete the .cplz and the temp directory
+ Directory.Delete(TempDirectory, true);
+ zfi.Delete();
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "Done with .cplz archives");
+ }
+
///
/// Loads plugin assemblies from the designated plugin directory, checks their compatibility with .NET 8, and loads
/// the compatible assemblies into the application.
- ///
- /// This method scans the plugin directory for all `.dll` files, verifies their compatibility
- /// with .NET 8, and attempts to load the compatible assemblies. Assemblies that are incompatible are logged as
- /// warnings and added to a list of incompatible plugins for further inspection.
- private static void LoadPluginAssemblies()
- {
- Debug.LogMessage(LogEventLevel.Information, "Loading assemblies from loadedPlugins folder...");
- var pluginDi = new DirectoryInfo(LoadedPluginsDirectoryPath);
- var pluginFiles = pluginDi.GetFiles("*.dll");
-
- Debug.LogMessage(LogEventLevel.Verbose, "Found {0} plugin assemblies to load", pluginFiles.Length);
-
- // First, check compatibility of all assemblies before loading any
- var assemblyCompatibility = new Dictionary References)>();
-
- foreach (var pluginFile in pluginFiles)
- {
- string fileName = pluginFile.Name;
- assemblyCompatibility[fileName] = IsPluginCompatibleWithNet8(pluginFile.FullName);
- }
-
- // Now load compatible assemblies and track incompatible ones
- foreach (var pluginFile in pluginFiles)
- {
+ ///
+ /// This method scans the plugin directory for all `.dll` files, verifies their compatibility
+ /// with .NET 8, and attempts to load the compatible assemblies. Assemblies that are incompatible are logged as
+ /// warnings and added to a list of incompatible plugins for further inspection.
+ private static void LoadPluginAssemblies()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Loading assemblies from loadedPlugins folder...");
+ var pluginDi = new DirectoryInfo(LoadedPluginsDirectoryPath);
+ var pluginFiles = pluginDi.GetFiles("*.dll");
+
+ Debug.LogMessage(LogEventLevel.Verbose, "Found {0} plugin assemblies to load", pluginFiles.Length);
+
+ // First, check compatibility of all assemblies before loading any
+ var assemblyCompatibility = new Dictionary References)>();
+
+ foreach (var pluginFile in pluginFiles)
+ {
string fileName = pluginFile.Name;
- var (isCompatible, reason, _) = assemblyCompatibility[fileName];
-
- if (!isCompatible)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
- IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, reason, null));
- continue;
- }
-
- // Try to load the assembly
- var loadedAssembly = LoadAssembly(pluginFile.FullName, null);
-
- if (loadedAssembly != null)
- {
- LoadedPluginFolderAssemblies.Add(loadedAssembly);
- }
- }
-
- Debug.LogMessage(LogEventLevel.Information, "All Plugins Loaded.");
- }
-
+ assemblyCompatibility[fileName] = IsPluginCompatibleWithNet8(pluginFile.FullName);
+ }
+
+ // Now load compatible assemblies and track incompatible ones
+ foreach (var pluginFile in pluginFiles)
+ {
+ string fileName = pluginFile.Name;
+ var (isCompatible, reason, _) = assemblyCompatibility[fileName];
+
+ if (!isCompatible)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Assembly '{0}' is not compatible with .NET 8: {1}", fileName, reason);
+ IncompatiblePlugins.Add(new IncompatiblePlugin(fileName, reason, null));
+ continue;
+ }
+
+ // Try to load the assembly
+ var loadedAssembly = LoadAssembly(pluginFile.FullName, null);
+
+ if (loadedAssembly != null)
+ {
+ LoadedPluginFolderAssemblies.Add(loadedAssembly);
+ }
+ }
+
+ Debug.LogMessage(LogEventLevel.Information, "All Plugins Loaded.");
+ }
+
///
/// Loads and initializes custom plugin types from the assemblies in the plugin folder.
- ///
- /// This method iterates through all loaded plugin assemblies, identifies types that implement
- /// the interface, and attempts to instantiate and load them. Assemblies or
- /// types that cannot be loaded due to missing dependencies, type loading errors, or other exceptions are logged,
- /// and incompatible plugins are tracked for further analysis.
- private static void LoadCustomPluginTypes()
- {
- Debug.LogMessage(LogEventLevel.Information, "Loading Custom Plugin Types...");
-
- foreach (var loadedAssembly in LoadedPluginFolderAssemblies)
- {
- // Skip if assembly is null (can happen if we had loading issues)
- if (loadedAssembly == null || loadedAssembly.Assembly == null)
- continue;
-
- // iteratate this assembly's classes, looking for "LoadPlugin()" methods
- try
- {
- var assy = loadedAssembly.Assembly;
- Type[] types = [];
- try
- {
- types = assy.GetTypes();
- Debug.LogMessage(LogEventLevel.Debug, $"Got types for assembly {assy.GetName().Name}");
- }
- catch (ReflectionTypeLoadException e)
- {
- Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
- loadedAssembly.Name, e.Message);
-
- // Check if any of the loader exceptions are due to missing assemblies
- foreach (var loaderEx in e.LoaderExceptions)
- {
- if (loaderEx is FileNotFoundException fileNotFoundEx)
- {
- string missingAssembly = fileNotFoundEx.FileName;
- if (!string.IsNullOrEmpty(missingAssembly))
- {
- Debug.LogMessage(LogEventLevel.Warning, "Assembly {0} requires missing dependency: {1}",
- loadedAssembly.Name, missingAssembly);
-
- // Add to incompatible plugins with dependency information
- IncompatiblePlugins.Add(new IncompatiblePlugin(
- Path.GetFileName(missingAssembly),
- $"Missing dependency required by {loadedAssembly.Name}",
- loadedAssembly.Name));
- }
- }
- }
-
- Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
- continue;
- }
- catch (TypeLoadException e)
- {
- Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
- loadedAssembly.Name, e.Message);
- Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
-
- // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
- if (e.Message.Contains("Could not load type") ||
- e.Message.Contains("Unable to load one or more of the requested types"))
- {
- IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
- $"Type loading error: {e.Message}",
- null));
- }
-
- continue;
- }
-
- foreach (var type in types)
- {
- try
+ ///
+ /// This method iterates through all loaded plugin assemblies, identifies types that implement
+ /// the interface, and attempts to instantiate and load them. Assemblies or
+ /// types that cannot be loaded due to missing dependencies, type loading errors, or other exceptions are logged,
+ /// and incompatible plugins are tracked for further analysis.
+ private static void LoadCustomPluginTypes()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Loading Custom Plugin Types...");
+
+ foreach (var loadedAssembly in LoadedPluginFolderAssemblies)
+ {
+ // Skip if assembly is null (can happen if we had loading issues)
+ if (loadedAssembly == null || loadedAssembly.Assembly == null)
+ continue;
+
+ // iteratate this assembly's classes, looking for "LoadPlugin()" methods
+ try
+ {
+ var assy = loadedAssembly.Assembly;
+ Type[] types = [];
+ try
+ {
+ types = assy.GetTypes();
+ Debug.LogMessage(LogEventLevel.Debug, $"Got types for assembly {assy.GetName().Name}");
+ }
+ catch (ReflectionTypeLoadException e)
+ {
+ Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
+ loadedAssembly.Name, e.Message);
+
+ // Check if any of the loader exceptions are due to missing assemblies
+ foreach (var loaderEx in e.LoaderExceptions)
+ {
+ if (loaderEx is FileNotFoundException fileNotFoundEx)
+ {
+ string missingAssembly = fileNotFoundEx.FileName;
+ if (!string.IsNullOrEmpty(missingAssembly))
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Assembly {0} requires missing dependency: {1}",
+ loadedAssembly.Name, missingAssembly);
+
+ // Add to incompatible plugins with dependency information
+ IncompatiblePlugins.Add(new IncompatiblePlugin(
+ Path.GetFileName(missingAssembly),
+ $"Missing dependency required by {loadedAssembly.Name}",
+ loadedAssembly.Name));
+ }
+ }
+ }
+
+ Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
+ continue;
+ }
+ catch (TypeLoadException e)
+ {
+ Debug.LogMessage(LogEventLevel.Error, "Unable to get types for assembly {0}: {1}",
+ loadedAssembly.Name, e.Message);
+ Debug.LogMessage(LogEventLevel.Verbose, e.StackTrace);
+
+ // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
+ if (e.Message.Contains("Could not load type") ||
+ e.Message.Contains("Unable to load one or more of the requested types"))
+ {
+ IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
+ $"Type loading error: {e.Message}",
+ null));
+ }
+
+ continue;
+ }
+
+ foreach (var type in types)
+ {
+ try
{
if (!typeof(IPluginDeviceFactory).IsAssignableFrom(type) || type.IsAbstract)
{
@@ -726,82 +725,82 @@ public static class PluginLoader
var plugin = (IPluginDeviceFactory)Activator.CreateInstance(type);
LoadCustomPlugin(plugin, loadedAssembly);
}
- catch (NotSupportedException)
- {
- //this happens for dlls that aren't PD dlls, like ports of Mono classes into S#. Swallowing.
- }
- catch (Exception ex)
- {
- Debug.LogError("Load Plugin not found. {assemblyName}.{typeName} is not a plugin factory. Exception: {exception}",
- loadedAssembly.Name, type.Name, ex.Message);
- continue;
- }
- }
- }
- catch (Exception e)
- {
- Debug.LogMessage(LogEventLevel.Information, "Error Loading assembly {0}: {1}",
- loadedAssembly.Name, e.Message);
- Debug.LogMessage(LogEventLevel.Verbose, "{0}", e.StackTrace);
-
- // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
- if (e.Message.Contains("Could not load type") ||
- e.Message.Contains("Unable to load one or more of the requested types"))
- {
- IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
- $"Assembly loading error: {e.Message}",
- null));
- }
-
- continue;
- }
- }
-
- // Update incompatible plugins with dependency information
- var pluginDependencies = new Dictionary>();
- // Populate pluginDependencies with relevant data
- // Example: pluginDependencies["PluginA"] = new List { "Dependency1", "Dependency2" };
- UpdateIncompatiblePluginDependencies(pluginDependencies);
-
- // plugin dll will be loaded. Any classes in plugin should have a static constructor
- // that registers that class with the Core.DeviceFactory
- Debug.LogMessage(LogEventLevel.Information, "Done Loading Custom Plugin Types.");
- }
-
+ catch (NotSupportedException)
+ {
+ //this happens for dlls that aren't PD dlls, like ports of Mono classes into S#. Swallowing.
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError("Load Plugin not found. {assemblyName}.{typeName} is not a plugin factory. Exception: {exception}",
+ loadedAssembly.Name, type.Name, ex.Message);
+ continue;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Error Loading assembly {0}: {1}",
+ loadedAssembly.Name, e.Message);
+ Debug.LogMessage(LogEventLevel.Verbose, "{0}", e.StackTrace);
+
+ // Add to incompatible plugins if this is likely a .NET 8 compatibility issue
+ if (e.Message.Contains("Could not load type") ||
+ e.Message.Contains("Unable to load one or more of the requested types"))
+ {
+ IncompatiblePlugins.Add(new IncompatiblePlugin(loadedAssembly.Name,
+ $"Assembly loading error: {e.Message}",
+ null));
+ }
+
+ continue;
+ }
+ }
+
+ // Update incompatible plugins with dependency information
+ var pluginDependencies = new Dictionary>();
+ // Populate pluginDependencies with relevant data
+ // Example: pluginDependencies["PluginA"] = new List { "Dependency1", "Dependency2" };
+ UpdateIncompatiblePluginDependencies(pluginDependencies);
+
+ // plugin dll will be loaded. Any classes in plugin should have a static constructor
+ // that registers that class with the Core.DeviceFactory
+ Debug.LogMessage(LogEventLevel.Information, "Done Loading Custom Plugin Types.");
+ }
+
///
/// Updates the triggering plugin information for incompatible plugins based on their dependencies.
///
/// This method iterates through a predefined list of incompatible plugins and updates their
/// triggering plugin information if they were directly loaded and are found to be dependencies of other plugins.
/// The update is performed for the first plugin that depends on the incompatible plugin.
- /// A dictionary where the key is the name of a plugin and the value is a list of its dependencies. Each dependency
- /// is represented as a string, which may include additional metadata.
- private static void UpdateIncompatiblePluginDependencies(Dictionary> pluginDependencies)
- {
- // For each incompatible plugin
- foreach (var incompatiblePlugin in IncompatiblePlugins)
- {
- // If it already has a requestedBy, skip it
- if (incompatiblePlugin.TriggeredBy != "Direct load")
- continue;
-
- // Find plugins that depend on this incompatible plugin
- foreach (var plugin in pluginDependencies)
- {
- string pluginName = plugin.Key;
- List dependencies = plugin.Value;
-
- // If this plugin depends on the incompatible plugin
- if (dependencies.Contains(incompatiblePlugin.Name) ||
- dependencies.Any(d => d.StartsWith(incompatiblePlugin.Name + ",")))
- {
- incompatiblePlugin.UpdateTriggeringPlugin(pluginName);
- break;
- }
- }
- }
- }
-
+ /// A dictionary where the key is the name of a plugin and the value is a list of its dependencies. Each dependency
+ /// is represented as a string, which may include additional metadata.
+ private static void UpdateIncompatiblePluginDependencies(Dictionary> pluginDependencies)
+ {
+ // For each incompatible plugin
+ foreach (var incompatiblePlugin in IncompatiblePlugins)
+ {
+ // If it already has a requestedBy, skip it
+ if (incompatiblePlugin.TriggeredBy != "Direct load")
+ continue;
+
+ // Find plugins that depend on this incompatible plugin
+ foreach (var plugin in pluginDependencies)
+ {
+ string pluginName = plugin.Key;
+ List dependencies = plugin.Value;
+
+ // If this plugin depends on the incompatible plugin
+ if (dependencies.Contains(incompatiblePlugin.Name) ||
+ dependencies.Any(d => d.StartsWith(incompatiblePlugin.Name + ",")))
+ {
+ incompatiblePlugin.UpdateTriggeringPlugin(pluginName);
+ break;
+ }
+ }
+ }
+ }
+
///
/// Loads a custom plugin and performs a dependency check to ensure compatibility with the required Essentials
/// framework version.
@@ -812,33 +811,33 @@ public static class PluginLoader
/// 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 deviceFactory, LoadedAssembly loadedAssembly)
- {
+ /// The assembly associated with the plugin being loaded. This is used for logging and tracking purposes.
+ private static void LoadCustomPlugin(IPluginDeviceFactory deviceFactory, LoadedAssembly loadedAssembly)
+ {
var developmentDeviceFactory = deviceFactory as IPluginDevelopmentDeviceFactory;
var passed = developmentDeviceFactory != null ? Global.IsRunningDevelopmentVersion
(developmentDeviceFactory.DevelopmentEssentialsFrameworkVersions, developmentDeviceFactory.MinimumEssentialsFrameworkVersion)
- : Global.IsRunningMinimumVersionOrHigher(deviceFactory.MinimumEssentialsFrameworkVersion);
-
- if (!passed)
- {
- 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.LogInformation("Passed plugin passed dependency check (required version {essentialsMinimumVersion})", deviceFactory.MinimumEssentialsFrameworkVersion);
- }
-
- Debug.LogInformation("Loading plugin: {pluginName}", loadedAssembly.Name);
+ : Global.IsRunningMinimumVersionOrHigher(deviceFactory.MinimumEssentialsFrameworkVersion);
- LoadDeviceFactories(deviceFactory);
-
- if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
- EssentialsPluginAssemblies.Add(loadedAssembly);
+ if (!passed)
+ {
+ 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.LogInformation("Passed plugin passed dependency check (required version {essentialsMinimumVersion})", deviceFactory.MinimumEssentialsFrameworkVersion);
+ }
+
+ Debug.LogInformation("Loading plugin: {pluginName}", loadedAssembly.Name);
+
+ LoadDeviceFactories(deviceFactory);
+
+ if(!EssentialsPluginAssemblies.Contains(loadedAssembly))
+ EssentialsPluginAssemblies.Add(loadedAssembly);
}
///
@@ -858,59 +857,59 @@ public static class PluginLoader
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.
- ///
- /// This method performs the following steps: - Checks if
- /// the plugin directory exists.
- Processes any plugin files, including .dll
- /// and .cplz files, by moving or extracting them as needed.
- Loads
- /// assemblies from the processed plugins into the application domain.
- /// - Identifies and reports any incompatible plugins, including the reason for
- /// incompatibility.
Plugins that are successfully loaded are made available for use,
- /// while incompatible plugins are logged for review.
- public static void LoadPlugins()
- {
- Debug.LogMessage(LogEventLevel.Information, "Attempting to Load Plugins from {_pluginDirectory}", PluginDirectory);
-
- if (Directory.Exists(PluginDirectory))
- {
- Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins");
-
- // Deal with any .dll files
- MoveDllAssemblies();
-
- // Deal with any .cplz files
- UnzipAndMoveCplzArchives();
-
- if (Directory.Exists(LoadedPluginsDirectoryPath))
- {
- // Load the assemblies from the loadedPlugins folder into the AppDomain
- LoadPluginAssemblies();
-
- // Load the types from any custom plugin assemblies
- LoadCustomPluginTypes();
- }
-
- // Report on incompatible plugins
- if (IncompatiblePlugins.Count > 0)
- {
- Debug.LogMessage(LogEventLevel.Warning, "Found {0} incompatible plugins:", IncompatiblePlugins.Count);
- foreach (var plugin in IncompatiblePlugins)
- {
- if (plugin.TriggeredBy != "Direct load")
- {
- Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1} (Required by: {2})",
- plugin.Name, plugin.Reason, plugin.TriggeredBy);
- }
- else
- {
- Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1}",
- plugin.Name, plugin.Reason);
- }
- }
- }
- }
- }
-}
+ ///
+ /// This method performs the following steps: - Checks if
+ /// the plugin directory exists.
- Processes any plugin files, including .dll
+ /// and .cplz files, by moving or extracting them as needed.
- Loads
+ /// assemblies from the processed plugins into the application domain.
+ /// - Identifies and reports any incompatible plugins, including the reason for
+ /// incompatibility.
Plugins that are successfully loaded are made available for use,
+ /// while incompatible plugins are logged for review.
+ public static void LoadPlugins()
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Attempting to Load Plugins from {_pluginDirectory}", PluginDirectory);
+
+ if (Directory.Exists(PluginDirectory))
+ {
+ Debug.LogMessage(LogEventLevel.Information, "Plugins directory found, checking for plugins");
+
+ // Deal with any .dll files
+ MoveDllAssemblies();
+
+ // Deal with any .cplz files
+ UnzipAndMoveCplzArchives();
+
+ if (Directory.Exists(LoadedPluginsDirectoryPath))
+ {
+ // Load the assemblies from the loadedPlugins folder into the AppDomain
+ LoadPluginAssemblies();
+
+ // Load the types from any custom plugin assemblies
+ LoadCustomPluginTypes();
+ }
+
+ // Report on incompatible plugins
+ if (IncompatiblePlugins.Count > 0)
+ {
+ Debug.LogMessage(LogEventLevel.Warning, "Found {0} incompatible plugins:", IncompatiblePlugins.Count);
+ foreach (var plugin in IncompatiblePlugins)
+ {
+ if (plugin.TriggeredBy != "Direct load")
+ {
+ Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1} (Required by: {2})",
+ plugin.Name, plugin.Reason, plugin.TriggeredBy);
+ }
+ else
+ {
+ Debug.LogMessage(LogEventLevel.Warning, " - {0}: {1}",
+ plugin.Name, plugin.Reason);
+ }
+ }
+ }
+ }
+ }
+}