
Renamed Utils to ModLoader Changed static class to static instance Moved logging to class and added file logging Added more protection for modloader properties Fixed RefreshModFiles now unloads mods before clearing Fixed trying to load abstract classes and using IsAssignableFrom Various small improvements
141 lines
4.4 KiB
C#
141 lines
4.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Reflection;
|
|
using UnityEngine;
|
|
|
|
namespace BWModLoader
|
|
{
|
|
public class ModLoader
|
|
{
|
|
public static ModLoader Instance { get; internal set; }
|
|
|
|
private static readonly string dllpath = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
|
|
private static readonly string folderPath = Path.GetDirectoryName(dllpath);
|
|
public static string ModsPath => folderPath + "\\Mods";
|
|
public static string AssetsPath => ModsPath + "\\Assets";
|
|
public static string LogPath => ModsPath + "\\Logs";
|
|
|
|
public ModLogger Logger;
|
|
|
|
/// <summary>
|
|
/// All known mods files
|
|
/// </summary>
|
|
private readonly Dictionary<FileInfo, List<Type>> allMods = new Dictionary<FileInfo, List<Type>>();
|
|
|
|
/// <summary>
|
|
/// Gets all known mods files
|
|
/// </summary>
|
|
/// Prevent modification from outside by making copy
|
|
public Dictionary<FileInfo, List<Type>> GetAllMods() => new Dictionary<FileInfo, List<Type>>(allMods);
|
|
|
|
/// <summary>
|
|
/// GameObject that holds our mods
|
|
/// </summary>
|
|
public GameObject ModObjects { get; } = new GameObject();
|
|
|
|
public ModLoader(ModLogger logger)
|
|
{
|
|
this.Logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a mod is loaded
|
|
/// </summary>
|
|
/// <param name="mod"></param>
|
|
/// <returns></returns>
|
|
public bool IsLoaded(Type mod)
|
|
{
|
|
return ModObjects.GetComponent(mod) != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refresh all known mods
|
|
/// </summary>
|
|
public void RefreshModFiles()
|
|
{
|
|
DirectoryInfo dir = new DirectoryInfo(ModsPath);
|
|
//Unloads & clears known mods
|
|
foreach (var mod in allMods)
|
|
{
|
|
Unload(mod.Key);
|
|
}
|
|
allMods.Clear();
|
|
|
|
//Find all files to refresh
|
|
foreach (FileInfo file in dir.GetFiles("*.dll"))
|
|
{
|
|
//Save mod types and file path
|
|
allMods.Add(file, LoadModTypes(file));
|
|
Logger.Log("Found dll: " + file.Name);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds and loads all mod classes in a file
|
|
/// </summary>
|
|
/// <param name="file">The file to load</param>
|
|
/// <returns></returns>
|
|
private List<Type> LoadModTypes(FileInfo file)
|
|
{
|
|
List<Type> mods = new List<Type>();
|
|
try
|
|
{
|
|
Assembly modDll = Assembly.LoadFrom(file.FullName);
|
|
Type[] modType = modDll.GetTypes();
|
|
foreach (Type t in modType)
|
|
{
|
|
Logger.Log("Found type in " + file.Name + ": " + t.Name);
|
|
if (t.IsClass && typeof(MonoBehaviour).IsAssignableFrom(t) && !t.IsAbstract && t.IsPublic)
|
|
{
|
|
mods.Add(t);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Logger.Log("Exception raised while loading mod " + file.Name);
|
|
Logger.Log(e.Message);
|
|
Logger.Log("Skipped loading this mod");
|
|
}
|
|
return mods;
|
|
}
|
|
|
|
/// <summary>
|
|
/// will load a mod from memory
|
|
/// </summary>
|
|
/// <param name="file"></param>
|
|
public void Load(FileInfo file)
|
|
{
|
|
if (allMods.TryGetValue(file, out var types))
|
|
{
|
|
foreach (Type mod in types)
|
|
{
|
|
//if mod is not loaded
|
|
if (!IsLoaded(mod))
|
|
{
|
|
ModObjects.AddComponent(mod);
|
|
Logger.Log("Loaded: " + mod.Name + " From file: " + file.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Unloads a mod from game
|
|
public void Unload(FileInfo file)
|
|
{
|
|
if (allMods.TryGetValue(file, out var types))
|
|
{
|
|
foreach (Type mod in types)
|
|
{
|
|
//if mod is loaded
|
|
if (IsLoaded(mod))
|
|
{
|
|
UnityEngine.Object.Destroy(ModObjects.GetComponent(mod));
|
|
Logger.Log("Unloaded: " + mod.Name + " From file: " + file.Name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |