private static void LoadMod(Mod mod) { if (!LoadedMods.Contains(mod)) { if (mod.HasTextures) { if (!Directory.Exists(ModsPath + "/" + mod.ID + "/Textures")) { Directory.CreateDirectory(ModsPath + "/" + mod.ID + "/Textures"); } } if (mod.HasAssetBundles) { if (!Directory.Exists(ModsPath + "/" + mod.ID + "/AssetBundles")) { Directory.CreateDirectory(ModsPath + "/" + mod.ID + "/AssetBundles"); } } mod.OnInit(); LoadedMods.Add(mod); ModLogs.Log("Loaded mod " + mod.ID); } else { ModLogs.Log("Mod " + mod.ID + " already loaded."); } }
/// <summary> /// Load a Mod. /// </summary> /// <param name="mod">The instance of the Mod to load.</param> /// <param name="isInternal">If the Mod is internal or not.</param> private static void LoadMod(Mod mod, bool isInternal = false) { // Check if mod already exists if (!LoadedMods.Contains(mod)) { // Generate config files if (!Directory.Exists(ConfigFolder + mod.ID)) { Directory.CreateDirectory(ConfigFolder + mod.ID); } // Load mod.OnLoad(); LoadedMods.Add(mod); if (!isInternal) { ModConsole.Print(string.Format("<color=lime><b>Mod Loaded:</b></color><color=orange><b>{0}</b></color>", mod.ID)); } else { //ModConsole.Print("Loaded internal mod: " + mod.ID); //debug } } else { ModConsole.Print(string.Format("<color=orange><b>Mod already loaded (or duplicated ID):</b></color><color=red><b>{0}</b></color>", mod.ID)); } }
/// <summary> /// Load a Mod. /// </summary> /// <param name="mod">The instance of the Mod to load.</param> /// <param name="isInternal">If the Mod is internal or not.</param> private static void LoadMod(Mod mod, bool isInternal = false) { // Check if mod already exists if (!LoadedMods.Contains(mod)) { // Generate config files if (!Directory.Exists(ConfigFolder + mod.ID)) { Directory.CreateDirectory(ConfigFolder + mod.ID); } // Load mod.OnLoad(); LoadedMods.Add(mod); if (!isInternal) { ModConsole.Print("Loaded mod: " + mod.ID); } else { ModConsole.Print("Loaded internal mod: " + mod.ID); } } else { ModConsole.Print("Mod already loaded: " + mod.ID); } }
private void UnloadMod(ModInfo mod) { LoadedMods.Remove(mod.ModID); if (mod.ModID != null) { UnloadedMods.Add(mod.ModID, mod); } mod.Active = false; }
public void LoadMod(ModInfo modInfo) { if (LoadedMods.Contains(modInfo)) { return; } if (!modInfo.IsBepinexMod) { return; } // Add to loaded mods already so we don't get infinite recursion LoadedMods.Add(modInfo); foreach (var dependency in modInfo.Manifest.Dependencies) { var versionless = dependency.Substring(0, dependency.LastIndexOf('-')); if (ModsToLoadByName.ContainsKey(dependency)) { LoadMod(ModsToLoadByName[dependency]); } else if (ModsToLoadByName.ContainsKey(versionless)) { LoadMod(ModsToLoadByName[versionless]); } } Logger.LogInfo($"Loading {modInfo.FullVersionName}..."); foreach (var dllPath in Directory.GetFiles(modInfo.Path, "*.dll", SearchOption.AllDirectories)) { try { AssemblyName assemblyName = AssemblyName.GetAssemblyName(dllPath); Assembly assembly = Assembly.Load(assemblyName); foreach (Type type in assembly.GetTypes()) { if (!type.IsInterface && !type.IsAbstract && typeof(BaseUnityPlugin).IsAssignableFrom(type)) { Chainloader.ManagerObject.AddComponent(type); Logger.LogInfo($" Loaded plugin {type.ToString()}"); } } } catch (BadImageFormatException) { } catch (ReflectionTypeLoadException ex) { Logger.LogError($"Could not load \"{Path.GetFileName(dllPath)}\"!"); Logger.LogDebug(Utils.TypeLoadExceptionToString(ex)); } } }
/// <summary> /// Loads the mod by searching for assemblies in hollow_knight_Data\Managed\Mods\ /// </summary> public static void LoadMods() { if (Loaded) { return; } Logger.Log("[API] - Trying to load mods"); string path = string.Empty; if (SystemInfo.operatingSystem.Contains("Windows")) { path = Application.dataPath + "\\Managed\\Mods"; } else if (SystemInfo.operatingSystem.Contains("Mac")) { path = Application.dataPath + "/Resources/Data/Managed/Mods/"; } else if (SystemInfo.operatingSystem.Contains("Linux")) { path = Application.dataPath + "/Managed/Mods"; } else { Logger.LogWarn($"Operating system of {SystemInfo.operatingSystem} is not known. Unable to load mods."); } if (string.IsNullOrEmpty(path)) { Loaded = true; return; } foreach (string text2 in Directory.GetFiles(path, "*.dll")) { Logger.LogDebug("[API] - Loading assembly: " + text2); try { foreach (Type type in Assembly.LoadFile(text2).GetExportedTypes()) { if (IsSubclassOfRawGeneric(typeof(Mod <>), type)) { Logger.LogDebug("[API] - Trying to instantiate mod<T>: " + type); IMod mod = Activator.CreateInstance(type) as IMod; if (mod == null) { continue; } LoadedMods.Add((Mod)mod); } else if (!type.IsGenericType && type.IsClass && type.IsSubclassOf(typeof(Mod))) { Logger.LogDebug("[API] - Trying to instantiate mod: " + type); Mod mod2 = type.GetConstructor(new Type[0])?.Invoke(new object[0]) as Mod; if (mod2 == null) { continue; } LoadedMods.Add(mod2); } } } catch (Exception ex) { Logger.LogError("[API] - Error: " + ex); Errors.Add(string.Concat(text2, ": FAILED TO LOAD! Check ModLog.txt.")); } } foreach (IMod mod in LoadedMods.OrderBy(x => x.LoadPriority())) { try { LoadMod(mod, false); } catch (Exception ex) { Errors.Add(string.Concat(mod.GetName(), ": FAILED TO LOAD! Check ModLog.txt.")); Logger.LogError("[API] - Error: " + ex); } } //Clean out the ModEnabledSettings for any mods that don't exist. //Calling ToList means we are not working with the dictionary keys directly, preventing an out of sync error foreach (string modName in ModHooks.Instance.GlobalSettings.ModEnabledSettings.Keys.ToList()) { if (LoadedMods.All(x => x.GetName() != modName)) { ModHooks.Instance.GlobalSettings.ModEnabledSettings.Remove(modName); } } GameObject gameObject = new GameObject(); _draw = gameObject.AddComponent <ModVersionDraw>(); UnityEngine.Object.DontDestroyOnLoad(gameObject); UpdateModText(); Loaded = true; ModHooks.Instance.SaveGlobalSettings(); }
private void ResolveModDependencies() { Manager.Info("Resolving mod dependencies..."); //First, detect if any currently loaded mods conflict with any other loaded mods, and if so, remove it. //This is a bit awkward as the first step, considering that the conflict might be resolved due to // the other mod failing its requirements later on in the process, but I can't find an elegant way // to solve this. Arbitrariness will have to do. foreach (var mod in LoadedMods.Values.ToList()) { var conflicts = mod.ConflictingMods.Where(x => LoadedMods.ContainsKey(x)); if (conflicts.Count() > 0) { Manager.Warn($"Mod '{mod.ModName}' declares it is incompatible with the following mods: {conflicts.ToCSVString()}."); if (CanIgnoreModConflict(mod.ModID)) { Manager.Warn($"Due to config settings, the above conflicts are being ignored. This may cause problems."); } else { Manager.Info($"Mod '{mod.ModName}' is being unloaded due to conflict. If this is not desired, alter the IgnoreModConflicts setting within {Constants.SettingsLocation}."); UnloadMod(mod); } } } //Next we check for cycles in the dependency graph. var(sorted, cycles) = GetGraph(LoadedMods.Values).TarjanSort(); //change this to continue, unload, or fail? if (cycles.Count() > 0) { Manager.Warn($"Warning! One or more circular dependencies in the requested mod load order have been detected."); Manager.Warn("The following cycles were found:"); List <string> errorMessages = new List <string>(); bool failure = false; foreach (var cycle in cycles) { var loop = cycle.Select(x => LoadedMods[x].ModName).ToList(); //this shows the loop a bit better in the log; instead of just [D -> E -> A], it shows [D -> E -> A -> D] loop.Add(LoadedMods[cycle[0]].ModName); string message = $"[{loop.ToStringList(" -> ")}]"; errorMessages.Add(message); Manager.Warn(message); failure = failure || !CanIgnoreCircularReferences(cycle); } if (failure) { throw new CircularReferenceException($"One or more disallowed circular mod dependencies were detected: {errorMessages.ToStringList()}. This error can be ignored by setting PermitCircularDependencies to All in the XGEF_settings.json."); } else { Manager.Warn("Compilation will continue, but note that mod loading order is now essentially arbitrary for the affected mods."); } } //Lastly, now that mods have been determined to be compatible, we'll check to see if anyone is // missing a required mod (possibly due to flunking out of the above steps). var mods = LoadedMods.Values.ToList(); for (int i = 0; i < mods.Count; i++) { var mod = mods[i]; if (!LoadedMods.ContainsKey(mod.ModID)) { continue; } var missing = mod.RequiredMods.Except(LoadedMods.Keys); if (missing.Count() > 0) { Manager.Warn($"Mod '{mod.ModName}' declares it requires the existence of the following mods: {missing.ToCSVString()}."); if (CanIgnoreModRequirement(mod.ModID)) { Manager.Warn($"Due to config settings, the above missing requirements are being ignored. This may cause problems."); } else { Manager.Warn($"Mod '{mod.ModName}' is being unloaded due to missing requirements. If this is not desired, alter the IgnoreModRequirements setting within {Constants.SettingsLocation}."); UnloadMod(mod); //if a mod is removed, there's a possibility that another mod becomes invalidated because of it. //thus, we start the entire loop over again. i = 0; } } } }