From 54ff3ca085c4408c5c0c87f99e08b8839d185ad6 Mon Sep 17 00:00:00 2001 From: Levi--G Date: Sun, 4 Aug 2019 03:33:33 +0200 Subject: [PATCH] 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 --- BWModLoader.csproj | 9 ++- src/Loader.cs | 39 ++++++++----- src/ModGUI.cs | 47 +++++++-------- src/ModLoader.cs | 141 +++++++++++++++++++++++++++++++++++++++++++++ src/ModLogger.cs | 59 +++++++++++++++++++ src/Utils.cs | 118 ------------------------------------- 6 files changed, 251 insertions(+), 162 deletions(-) create mode 100644 src/ModLoader.cs create mode 100644 src/ModLogger.cs delete mode 100644 src/Utils.cs diff --git a/BWModLoader.csproj b/BWModLoader.csproj index 0bd4721..12881a9 100644 --- a/BWModLoader.csproj +++ b/BWModLoader.csproj @@ -1,4 +1,4 @@ - + Debug @@ -35,14 +35,13 @@ + - + - - - + \ No newline at end of file diff --git a/src/Loader.cs b/src/Loader.cs index 380819f..9d4c0c7 100644 --- a/src/Loader.cs +++ b/src/Loader.cs @@ -3,40 +3,47 @@ using System.Collections.Generic; using System.IO; using System.Reflection; using UnityEngine; -namespace ModLoader + +namespace BWModLoader { public static class Loader { public static void Load() { - Utils.Log("Starting mod loader..."); - Utils.DebugLog("Mods dir: "+Utils.modsPath); + string logfile = null; +#if DEBUG + logfile = ModLoader.LogPath + "\\modloader.log"; +#endif + ModLogger logger = new ModLogger("[BWML]", logfile); + logger.ClearLog(); + logger.Log("Starting mod loader..."); + logger.DebugLog("Mods dir: " + ModLoader.ModsPath); - if (!Directory.Exists(Utils.modsPath)) + if (!Directory.Exists(ModLoader.ModsPath)) { - Directory.CreateDirectory(Utils.modsPath); + Directory.CreateDirectory(ModLoader.ModsPath); } - if (!Directory.Exists(Utils.assetsPath)) + if (!Directory.Exists(ModLoader.AssetsPath)) { - Directory.CreateDirectory(Utils.assetsPath); + Directory.CreateDirectory(ModLoader.AssetsPath); } - Utils.modObjects = new GameObject(); - //For each DLL in "Blackwake/Blackwake_Data/Managed/Mods/" //Open them, Get the mod class, then add it in the game. - Utils.RefreshModFiles(); - foreach(FileInfo file in Utils.allMods.Keys) + ModLoader loader = new ModLoader(logger); + ModLoader.Instance = loader; + loader.RefreshModFiles(); + foreach (FileInfo file in loader.GetAllMods().Keys) { - Utils.Load(file); + loader.Load(file); } - Utils.Log("All Mods have been Loaded!"); - Utils.modObjects.AddComponent(); - Utils.Log("GUI has been loaded"); + logger.Log("All Mods have been Loaded!"); + loader.ModObjects.AddComponent(); + logger.Log("GUI has been loaded"); //Keep mods active - UnityEngine.Object.DontDestroyOnLoad(Utils.modObjects); + UnityEngine.Object.DontDestroyOnLoad(loader.ModObjects); } } } diff --git a/src/ModGUI.cs b/src/ModGUI.cs index 82b0a33..51d6387 100644 --- a/src/ModGUI.cs +++ b/src/ModGUI.cs @@ -1,10 +1,10 @@ -using System; +using System; using System.Collections; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using UnityEngine; -namespace ModGUI +namespace BWModLoader.ModGUI { public class ModGUI : MonoBehaviour { @@ -57,51 +57,52 @@ namespace ModGUI void LogWindow() { - GUI.Label(new Rect(0, 100, 100, 25), "LogWindow"); - int logNum = 0; - if (ModLoader.Utils.logs.Any()) - { + GUI.Label(new Rect(0, 100, 100, 25), "LogWindow"); + int logNum = 0; + if (ModLoader.Instance.Logger.Logs.Any()) + { scrollPosition = GUI.BeginScrollView(new Rect(0, 100, size.x, size.y - 100), scrollPosition, new Rect(0, 0, size.x, 50)); - foreach (string log in ModLoader.Utils.logs) + foreach (string log in ModLoader.Instance.Logger.Logs) { logNum++; GUI.Label(new Rect(0, 25 * logNum, 1000, 25), log); - } - GUI.EndScrollView(); + } + GUI.EndScrollView(); } } void ModWindow() { if (GUI.Button(new Rect(0, 100, 100, 25), "Reload all mods")) { - ModLoader.Utils.RefreshModFiles(); + ModLoader.Instance.RefreshModFiles(); } scrollPosition = GUI.BeginScrollView(new Rect(0, 100, size.x, size.y-100), scrollPosition, new Rect(0, 0, size.x, 50)); int modNum = 0; - foreach (FileInfo file in ModLoader.Utils.allMods.Keys) - { - foreach (Type mod in ModLoader.Utils.allMods[file]) + var allmods = ModLoader.Instance.GetAllMods(); + foreach (FileInfo file in allmods.Keys) + { + foreach (Type mod in allmods[file]) { modNum++; - GUI.Label(new Rect(0, modNum * 25, 100, 25), mod.Name); - if (!ModLoader.Utils.IsLoaded(mod)) + GUI.Label(new Rect(0, modNum * 25, 100, 25), mod.Name); + if (!ModLoader.Instance.IsLoaded(mod)) { if (GUI.Button(new Rect(100, modNum * 25, 100, 25), "Enable")) { - ModLoader.Utils.Load(file); - } - } + ModLoader.Instance.Load(file); + } + } else { if (GUI.Button(new Rect(100, modNum * 25, 100, 25), "Disable")) { - ModLoader.Utils.Unload(file); - } + ModLoader.Instance.Unload(file); + } } if (GUI.Button(new Rect(200, modNum * 25, 100, 25), "Reload")) { - ModLoader.Utils.Unload(file); - ModLoader.Utils.RefreshModFiles(); + ModLoader.Instance.Unload(file); + ModLoader.Instance.RefreshModFiles(); } } } diff --git a/src/ModLoader.cs b/src/ModLoader.cs new file mode 100644 index 0000000..aa5ef07 --- /dev/null +++ b/src/ModLoader.cs @@ -0,0 +1,141 @@ +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; + + /// + /// All known mods files + /// + private readonly Dictionary> allMods = new Dictionary>(); + + /// + /// Gets all known mods files + /// + /// Prevent modification from outside by making copy + public Dictionary> GetAllMods() => new Dictionary>(allMods); + + /// + /// GameObject that holds our mods + /// + public GameObject ModObjects { get; } = new GameObject(); + + public ModLoader(ModLogger logger) + { + this.Logger = logger; + } + + /// + /// Checks if a mod is loaded + /// + /// + /// + public bool IsLoaded(Type mod) + { + return ModObjects.GetComponent(mod) != null; + } + + /// + /// Refresh all known mods + /// + 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); + } + } + + /// + /// Finds and loads all mod classes in a file + /// + /// The file to load + /// + private List LoadModTypes(FileInfo file) + { + List mods = new List(); + 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; + } + + /// + /// will load a mod from memory + /// + /// + 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); + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ModLogger.cs b/src/ModLogger.cs new file mode 100644 index 0000000..06b52b8 --- /dev/null +++ b/src/ModLogger.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace BWModLoader +{ + public class ModLogger + { + string prefix; + string file; + + /// + /// Log history + /// + public List Logs { get; } = new List(); + + public ModLogger(string prefix, string file = null) + { + this.prefix = prefix; + this.file = file; + if (!string.IsNullOrEmpty(file) && !File.Exists(file)) + { + Directory.CreateDirectory(Path.GetDirectoryName(file)); + File.WriteAllText(file, ""); + } + } + + public void ClearLog() + { + Logs.Clear(); + if (!string.IsNullOrEmpty(file) && File.Exists(file)) + { + File.Delete(file); + } + } + + /// + /// Log to debugger and output.txt + /// + public void Log(string output) + { + Console.WriteLine(prefix + output); + Logs.Add(prefix + output); + if (!string.IsNullOrEmpty(file)) + { + File.AppendAllText(file, prefix + output + Environment.NewLine); + } + } + + public void DebugLog(string output) + { +#if DEBUG + Log(output); +#endif // DEBUG + } + } +} diff --git a/src/Utils.cs b/src/Utils.cs deleted file mode 100644 index 94dba98..0000000 --- a/src/Utils.cs +++ /dev/null @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using UnityEngine; -namespace ModLoader -{ - public static class Utils - { - static readonly string dllpath = new System.Uri(System.Reflection.Assembly.GetExecutingAssembly().CodeBase).LocalPath; - static readonly string path = new FileInfo(dllpath).Directory.FullName; - public static string modsPath = path + "\\Mods"; - public static string assetsPath = modsPath + "\\Assets"; - - //all known mods files and their classes - public static Dictionary> allMods = new Dictionary>(); - //Log history - public static List logs = new List(); - //Object that holds our mods - public static GameObject modObjects; - - //checks if mod is loaded - public static bool IsLoaded(Type mod) - { - if (modObjects.GetComponent(mod) != null) - { - return true; - } - return false; - } - //Refresh all known mods - public static void RefreshModFiles() - { - DirectoryInfo dir = new DirectoryInfo(modsPath); - //Clears known mods - allMods.Clear(); - - //Find all files to refresh - foreach (FileInfo file in dir.GetFiles("*.dll")) - { - - allMods.Add(file, new List()); - foreach(Type mod in FindModTypes(file)) - { - //Save mod type and file path - allMods[file].Add(mod); - } - Log("Found dll: " + file.Name); - } - } - //Find all mod classes in a file - static List FindModTypes(FileInfo file) - { - List mods = new List(); - try - { - Assembly modDll = Assembly.LoadFrom(file.FullName); - Type[] modType = modDll.GetTypes(); - foreach (Type t in modType) - { - Log("Found type in " + file.Name + ": " + t.Name); - if (t.IsClass && t.IsSubclassOf(typeof(MonoBehaviour))) - { - mods.Add(t); - } - } - } - catch (Exception e) - { - Log("Exception raised while loading mod " + file.Name); - Log(e.Message); - Log("Skipped loading this mod"); - } - return mods; - } - //will load a mod from memory - public static void Load(FileInfo file) - { - if (allMods.ContainsKey(file)) - { - foreach (Type mod in allMods[file]) - { - //if mod is not loaded - if (modObjects.GetComponent(mod) == null) - { - modObjects.AddComponent(mod); - Log("Loaded: " + mod.Name + " From file: " + file.Name); - } - } - } - } - //Unloads a mod from game - public static void Unload(FileInfo file) - { - foreach(Type mod in allMods[file]) - { - //if mod is loaded - if (modObjects.GetComponent(mod) != null) - { - UnityEngine.Object.Destroy(modObjects.GetComponent(mod)); - Log("Unloaded: " + mod.Name + " From file: " + file.Name); - } - } - } - //Log to debugger and output.txt - public static void Log(string output) - { - Console.WriteLine("[BWML]" + output); - logs.Add(output); - } - public static void DebugLog(string output) - { - #if DEBUG - Log(output); - #endif // DEBUG - } - } -}