/// <summary> /// Loads the path from the specified XML element. /// </summary> /// <param name="element">The XML element to load from.</param> /// <param name="helper">The XML configuration helper being used.</param> internal void LoadFromXmlElement(XElement element, ConfigurationFileHelper helper) { if (element == null) { return; } //Read source Source = helper.ReadStringAttribute( element, "src" ); //Read port CreateDirectory = helper.ReadBooleanAttribute( element, "createDir", false ); // Read dependency resolution strategy DependencyResolutionStrategy = helper.ReadDependencyResolutionStrategy( element, "dependencyResolutionStrategy", DependencyResolutionStrategy.RecursiveFromFile ); }
/// <summary> /// Adds a path to the DarkRift server that will be searched for plugins. /// </summary> /// <param name="source">The path to search.</param> /// <param name="dependencyResolutionStrategy">How dependencies for plugins loaded from this path should be resolved.</param> /// <returns>The configuration builder to continue construction.</returns> public DarkRiftServerConfigurationBuilder AddPluginSearchPath(string source, DependencyResolutionStrategy dependencyResolutionStrategy) { ServerSpawnData.PluginSearchSettings.PluginSearchPath pluginSearchPath = new ServerSpawnData.PluginSearchSettings.PluginSearchPath { Source = source, DependencyResolutionStrategy = dependencyResolutionStrategy }; ServerSpawnData.PluginSearch.PluginSearchPaths.Add(pluginSearchPath); return(this); }
/// <summary> /// Adds a directory of plugin files to the index. /// </summary> /// <param name="directory">The directory to add.</param> /// <param name="create">Whether to create the directory if not present.</param> /// <param name="dependencyResolutionStrategy">The way to resolve dependencies for the plugin.</param> internal void AddDirectory(string directory, bool create, DependencyResolutionStrategy dependencyResolutionStrategy) { //Create plugin directory if not present if (Directory.Exists(directory)) { //Get the names of all files to try and load string[] pluginSourceFiles = Directory.GetFiles(directory, "*.dll", SearchOption.AllDirectories); AddFiles(pluginSourceFiles, dependencyResolutionStrategy, directory); } else { if (create) Directory.CreateDirectory(directory); } }
/// <summary> /// Reads a DependencyResolutionStrategy value from the XML element supplied. /// </summary> /// <param name="element">The element to read from.</param> /// <param name="attributeName">The name of the attribute to read.</param> /// <param name="defaultValue">The default value to return if the attribute isn't present.</param> /// <returns>The string read.</returns> internal DependencyResolutionStrategy ReadDependencyResolutionStrategy(XElement element, string attributeName, DependencyResolutionStrategy defaultValue) { var attribute = element.Attribute(attributeName); if (attribute == null) { return(defaultValue); } switch (ResolveVariables(attribute.Value, attribute).Trim().ToLower()) { case "standard": return(DependencyResolutionStrategy.Standard); case "recursivefromfile": return(DependencyResolutionStrategy.RecursiveFromFile); case "recursivefromdirectory": return(DependencyResolutionStrategy.RecursiveFromDirectory); default: // TODO docs throw new XmlConfigurationException($"<{element.Name}> attribute '{attributeName}' is not a dependency resolution strategy. Expected 'standard', 'recursivefromfile' or 'recursivefromdirectory'.", $"{configurationDocsRoot}{element.Name}.html", attribute); } }
/// <summary> /// Adds the given plugin files into the index. /// </summary> /// <param name="files">An array of filepaths to the plugins.</param> /// <param name="dependencyResolutionStrategy">The way to resolve dependencies for the plugin.</param> /// <param name="searchedDirectory">the directory that was searched to find this file.</param> internal void AddFiles(IEnumerable<string> files, DependencyResolutionStrategy dependencyResolutionStrategy, string searchedDirectory) { //Load each file to a plugin foreach (string pluginSourceFile in files) AddFile(pluginSourceFile, dependencyResolutionStrategy, searchedDirectory); }
/// <summary> /// Adds all plugin types in the file to the index. /// </summary> /// <param name="file">The file containing the types.</param> /// <param name="dependencyResolutionStrategy">The way to resolve dependencies for the plugin.</param> /// <param name="searchedDirectory">the directory that was searched to find this file.</param> internal void AddFile(string file, DependencyResolutionStrategy dependencyResolutionStrategy, string searchedDirectory) { //Check the file is a dll if (Path.GetExtension(file) != ".dll") throw new ArgumentException("The filepath supplied was not a DLL library."); //Check the file exists if (!File.Exists(file)) throw new FileNotFoundException("The specified filepath does not exist."); //Log logger.Trace($"Searching '{file}' for plugins."); // Setup assembly resolver to help find dependencies recursively in the folder heirachy of the plugin AppDomain.CurrentDomain.AssemblyResolve += LoadFromSameFolder; Assembly LoadFromSameFolder(object sender, ResolveEventArgs args) { string rootFolderPath; if (dependencyResolutionStrategy == DependencyResolutionStrategy.RecursiveFromFile) { rootFolderPath = Path.GetDirectoryName(file); } else if (searchedDirectory != null) { rootFolderPath = searchedDirectory; } else { return null; } string assemblyPath = SearchForFile(rootFolderPath, new AssemblyName(args.Name).Name + ".dll"); if (assemblyPath == null) return null; return Assembly.LoadFrom(assemblyPath); } //Load the assembly Assembly assembly; try { assembly = Assembly.LoadFrom(Path.GetFullPath(file)); } catch (Exception e) { logger.Error($"{file} could not be loaded as an exception occurred.", e); return; } //Get the types in the assembly IEnumerable<Type> enclosedTypes; try { enclosedTypes = assembly.GetTypes(); } catch (ReflectionTypeLoadException e) { logger.Error( $"Failed to load one or more plugins from DLL file '{file}', see following logs for more info.\n\nIf this file is a DarkRift plugin rebuilding this plugin may help. Make sure it is built against the same .NET target as DarkRift is running and built to a compatible version ({Environment.Version}).\n\nThis exception can also occur when an unmanaged DLL is loaded by DarkRift because it is in a plugin search path. If this is the case, consider moving the unmanaged library out of any plugin search paths (https://docs.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing#unmanaged-native-library-probing) or modify the plugin search path configuration to avoid discovering the library (e.g. reference individual DLL files instead of the containing directory).", e ); foreach (Exception loaderException in e.LoaderExceptions) { logger.Error("Additional exception detail from LoaderExceptions property:", loaderException); } // The types unable to be loaded will be null here! enclosedTypes = e.Types.Where(t => t != null); } //Find the types that are plugins foreach (Type enclosedType in enclosedTypes) { if (enclosedType.IsSubclassOf(typeof(PluginBase)) && !enclosedType.IsAbstract) { //Add the plugin AddType(enclosedType); } } // Remove resolver again AppDomain.CurrentDomain.AssemblyResolve -= LoadFromSameFolder; }