BWModLoader/src/ModLoader.cs
Levi--G 54ff3ca085 Big Cleanup (#4)
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
2019-08-03 18:33:33 -07:00

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);
}
}
}
}
}
}