From b992cd37d813dc57fec818aedd2b43affe5a05d0 Mon Sep 17 00:00:00 2001 From: artman41 Date: Tue, 15 May 2018 13:44:44 +0100 Subject: [PATCH] - Added logger - Fixed ModPrefs to be per mod - Fixed the patcher so that it correctly detected the project - Removed depreciated methods within IPlugin - Instead added methods linked to SceneManager which correctly trigger --- IPA/PatchContext.cs | 2 +- IPA/Program.cs | 2 + IllusionInjector/CompositePlugin.cs | 95 ++++++++++------------ IllusionInjector/PluginComponent.cs | 23 ++++-- IllusionInjector/PluginManager.cs | 2 +- IllusionPlugin/IPlugin.cs | 21 +++-- IllusionPlugin/IniFile.cs | 22 ++++-- IllusionPlugin/Logger.cs | 117 ++++++++++++++++++++++++++++ IllusionPlugin/ModPrefs.cs | 57 +++++++------- 9 files changed, 238 insertions(+), 103 deletions(-) create mode 100644 IllusionPlugin/Logger.cs diff --git a/IPA/PatchContext.cs b/IPA/PatchContext.cs index 2ca0e519..9867d210 100644 --- a/IPA/PatchContext.cs +++ b/IPA/PatchContext.cs @@ -49,7 +49,7 @@ namespace IPA context.ProjectName = Path.GetFileNameWithoutExtension(context.Executable); context.DataPathDst = Path.Combine(context.ProjectRoot, context.ProjectName + "_Data"); context.ManagedPath = Path.Combine(context.DataPathDst, "Managed"); - context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngineCore.dll"); + context.EngineFile = Path.Combine(context.ManagedPath, "UnityEngine.CoreModule.dll"); context.AssemblyFile = Path.Combine(context.ManagedPath, "Assembly-CSharp.dll"); context.BackupPath = Path.Combine(Path.Combine(context.IPARoot, "Backups"), context.ProjectName); string shortcutName = string.Format("{0} (Patch & Launch)", context.ProjectName); diff --git a/IPA/Program.cs b/IPA/Program.cs index d188f204..fc8ce4dd 100644 --- a/IPA/Program.cs +++ b/IPA/Program.cs @@ -56,6 +56,8 @@ namespace IPA if (!Directory.Exists(c.DataPathDst) || !File.Exists(c.EngineFile)) { Fail("Game does not seem to be a Unity project. Could not find the libraries to patch."); + Console.WriteLine($"DataPath: {c.DataPathDst}"); + Console.WriteLine($"EngineFile: {c.EngineFile}"); } } diff --git a/IllusionInjector/CompositePlugin.cs b/IllusionInjector/CompositePlugin.cs index 1e4de7f9..193b702f 100644 --- a/IllusionInjector/CompositePlugin.cs +++ b/IllusionInjector/CompositePlugin.cs @@ -3,107 +3,94 @@ using System; using System.Collections.Generic; using System.Text; using UnityEngine; +using UnityEngine.SceneManagement; -namespace IllusionInjector -{ - public class CompositePlugin : IPlugin - { +namespace IllusionInjector { + public class CompositePlugin : IPlugin { IEnumerable plugins; private delegate void CompositeCall(IPlugin plugin); - public CompositePlugin(IEnumerable plugins) - { - this.plugins = plugins; + public CompositePlugin(IEnumerable plugins) { + this.plugins = plugins; } - public void OnApplicationStart() - { + public void OnApplicationStart() { Invoke(plugin => plugin.OnApplicationStart()); } - public void OnApplicationQuit() - { - Invoke(plugin => plugin.OnApplicationQuit()); + public void OnApplicationQuit() { + Invoke(plugin => plugin.OnApplicationQuit()); } - public void OnLevelWasLoaded(int level) - { - foreach (var plugin in plugins) - { - try - { - plugin.OnLevelWasLoaded(level); + public void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { + foreach (var plugin in plugins) { + try { + plugin.OnSceneLoaded(scene, sceneMode); } - catch (Exception ex) - { + catch (Exception ex) { Console.WriteLine("{0}: {1}", plugin.Name, ex); } } } - - private void Invoke(CompositeCall callback) - { - foreach (var plugin in plugins) - { - try - { - callback(plugin); + public void OnSceneUnloaded(Scene scene) { + foreach (var plugin in plugins) { + try { + plugin.OnSceneUnloaded(scene); } - catch (Exception ex) - { + catch (Exception ex) { Console.WriteLine("{0}: {1}", plugin.Name, ex); } } } + public void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { + foreach (var plugin in plugins) { + try { + plugin.OnActiveSceneChanged(prevScene, nextScene); + } + catch (Exception ex) { + Console.WriteLine("{0}: {1}", plugin.Name, ex); + } + } + } - public void OnLevelWasInitialized(int level) - { - foreach (var plugin in plugins) - { - try - { - plugin.OnLevelWasInitialized(level); + private void Invoke(CompositeCall callback) { + foreach (var plugin in plugins) { + try { + callback(plugin); } - catch (Exception ex) - { + catch (Exception ex) { Console.WriteLine("{0}: {1}", plugin.Name, ex); } } } - public void OnUpdate() - { + public void OnUpdate() { Invoke(plugin => plugin.OnUpdate()); } - public void OnFixedUpdate() - { + public void OnFixedUpdate() { Invoke(plugin => plugin.OnFixedUpdate()); } - public string Name - { + public string Name { get { throw new NotImplementedException(); } } - public string Version - { + public string Version { get { throw new NotImplementedException(); } } - public void OnLateUpdate() - { - Invoke(plugin => - { + public void OnLateUpdate() { + Invoke(plugin => { if (plugin is IEnhancedPlugin) - ((IEnhancedPlugin)plugin).OnLateUpdate(); + ((IEnhancedPlugin) plugin).OnLateUpdate(); }); } } -} +} \ No newline at end of file diff --git a/IllusionInjector/PluginComponent.cs b/IllusionInjector/PluginComponent.cs index 0214398b..4b285e34 100644 --- a/IllusionInjector/PluginComponent.cs +++ b/IllusionInjector/PluginComponent.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Text; using UnityEngine; +using UnityEngine.SceneManagement; namespace IllusionInjector { @@ -22,11 +23,10 @@ namespace IllusionInjector plugins = new CompositePlugin(PluginManager.Plugins); plugins.OnApplicationStart(); - } - - void Start() - { - OnLevelWasLoaded(Application.loadedLevel); + + SceneManager.activeSceneChanged += OnActiveSceneChanged; + SceneManager.sceneLoaded += OnSceneLoaded; + SceneManager.sceneUnloaded += OnSceneUnloaded; } void Update() @@ -34,7 +34,6 @@ namespace IllusionInjector if (freshlyLoaded) { freshlyLoaded = false; - plugins.OnLevelWasInitialized(Application.loadedLevel); } plugins.OnUpdate(); } @@ -64,11 +63,19 @@ namespace IllusionInjector quitting = true; } - void OnLevelWasLoaded(int level) + void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode) { - plugins.OnLevelWasLoaded(level); + plugins.OnSceneLoaded(scene, sceneMode); freshlyLoaded = true; } + + private void OnSceneUnloaded(Scene scene) { + plugins.OnSceneUnloaded(scene); + } + + private void OnActiveSceneChanged(Scene prevScene, Scene nextScene) { + plugins.OnActiveSceneChanged(prevScene, nextScene); + } } } diff --git a/IllusionInjector/PluginManager.cs b/IllusionInjector/PluginManager.cs index 5e7444b4..24cced6e 100644 --- a/IllusionInjector/PluginManager.cs +++ b/IllusionInjector/PluginManager.cs @@ -89,7 +89,7 @@ namespace IllusionInjector filter = ((IEnhancedPlugin)pluginInstance).Filter; } - if(filter == null || Enumerable.Contains(filter, exeName, StringComparer.OrdinalIgnoreCase)) + if(filter == null || filter.Contains(exeName, StringComparer.OrdinalIgnoreCase)) plugins.Add(pluginInstance); } catch (Exception e) diff --git a/IllusionPlugin/IPlugin.cs b/IllusionPlugin/IPlugin.cs index 14223a24..7d3ffea8 100644 --- a/IllusionPlugin/IPlugin.cs +++ b/IllusionPlugin/IPlugin.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Text; +using UnityEngine.SceneManagement; namespace IllusionPlugin { @@ -32,16 +33,24 @@ namespace IllusionPlugin void OnApplicationQuit(); /// - /// Gets invoked whenever a level is loaded. + /// Gets invoked whenever a scene is loaded. /// - /// - void OnLevelWasLoaded(int level); + /// The scene currently loaded + /// The type of loading + void OnSceneLoaded(Scene scene, LoadSceneMode sceneMode); /// - /// Gets invoked after the first update cycle after a level was loaded. + /// Gets invoked whenever a scene is unloaded /// - /// - void OnLevelWasInitialized(int level); + /// The unloaded scene + void OnSceneUnloaded(Scene scene); + + /// + /// Gets invoked whenever a scene is changed + /// + /// The Scene that was previously loaded + /// The Scene being loaded + void OnActiveSceneChanged(Scene prevScene, Scene nextScene); /// /// Gets invoked on every graphic update. diff --git a/IllusionPlugin/IniFile.cs b/IllusionPlugin/IniFile.cs index d773d9b6..1c6a435b 100644 --- a/IllusionPlugin/IniFile.cs +++ b/IllusionPlugin/IniFile.cs @@ -33,7 +33,7 @@ namespace IllusionPlugin string lpValue, string lpFileName); - private string _path = ""; + /*private string _path = ""; public string Path { get @@ -46,15 +46,27 @@ namespace IllusionPlugin File.WriteAllText(value, "", Encoding.Unicode); _path = value; } + }*/ + + private FileInfo _iniFileInfo; + public FileInfo IniFileInfo { + get => _iniFileInfo; + set { + _iniFileInfo = value; + if (_iniFileInfo.Exists) return; + _iniFileInfo.Directory?.Create(); + _iniFileInfo.Create(); + } } /// /// INIFile Constructor. /// /// - public IniFile(string INIPath) + public IniFile(string iniPath) { - this.Path = INIPath; + IniFileInfo = new FileInfo(iniPath); + //this.Path = INIPath; } /// @@ -68,7 +80,7 @@ namespace IllusionPlugin /// Value Name public void IniWriteValue(string Section, string Key, string Value) { - WritePrivateProfileString(Section, Key, Value, this.Path); + WritePrivateProfileString(Section, Key, Value, IniFileInfo.FullName); } /// @@ -82,7 +94,7 @@ namespace IllusionPlugin { const int MAX_CHARS = 1023; StringBuilder result = new StringBuilder(MAX_CHARS); - GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, this.Path); + GetPrivateProfileString(Section, Key, "", result, MAX_CHARS, IniFileInfo.FullName); return result.ToString(); } } diff --git a/IllusionPlugin/Logger.cs b/IllusionPlugin/Logger.cs new file mode 100644 index 00000000..3c201ce0 --- /dev/null +++ b/IllusionPlugin/Logger.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using IllusionPlugin; + +namespace IllusionPlugin { + public class Logger { + private readonly Queue _logQueue; + private readonly FileInfo _logFile; + private readonly Thread _watcherThread; + private bool _threadRunning; + + private logMessage oldLog; + + struct logMessage { + public WarningLevel WarningLevel; + public string Message; + + public logMessage(string msg, WarningLevel wl) { + Message = msg; + WarningLevel = wl; + } + } + + enum WarningLevel { + Log, Error, Exception + } + + Logger() { + _logQueue = new Queue(); + _logFile = GetPath("Default"); + _watcherThread = new Thread(QueueWatcher) {IsBackground = true}; + _threadRunning = true; + Start(); + } + + public Logger(IPlugin plugin) { + _logQueue = new Queue(); + _logFile = GetPath(plugin); + _watcherThread = new Thread(QueueWatcher) {IsBackground = true}; + _threadRunning = true; + Start(); + } + + public void Log(string msg) { + if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); + _logQueue.Enqueue(new logMessage($"[LOG @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Log)); + } + + public void Error(string msg) { + if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); + _logQueue.Enqueue(new logMessage($"[ERROR @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Error)); + } + + public void Exception(string msg) { + if(!_watcherThread.IsAlive) throw new Exception("Logger is Closed!"); + _logQueue.Enqueue(new logMessage($"[EXCEPTION @ {DateTime.Now:HH:mm:ss}] {msg}", WarningLevel.Exception)); + } + + void QueueWatcher() { + _logFile.Create().Close(); + SetConsoleColour(WarningLevel.Log); + while (_threadRunning) { + if (_logQueue.Count > 0) { + _watcherThread.IsBackground = false; + using (var f = _logFile.AppendText()) { + while (_logQueue.Count > 0) { + var d = _logQueue.Dequeue(); + if (d.Message == oldLog.Message) return; + oldLog = d; + f.WriteLine(d.Message); + if(d.WarningLevel != oldLog.WarningLevel) SetConsoleColour(d.WarningLevel); + Console.WriteLine(d); + } + } + + _watcherThread.IsBackground = true; + } + } + } + + void Start() => _watcherThread.Start(); + + public void Stop() { + _threadRunning = false; + _watcherThread.Join(); + } + + void SetConsoleColour(WarningLevel level) { + switch (level) { + case WarningLevel.Log: + Console.ForegroundColor = ConsoleColor.Green; + break; + case WarningLevel.Error: + Console.ForegroundColor = ConsoleColor.Yellow; + break; + case WarningLevel.Exception: + Console.ForegroundColor = ConsoleColor.Red; + break; + } + } + + FileInfo GetPath(IPlugin plugin) => GetPath(plugin.Name); + FileInfo GetPath(string modName) { + var logsDir = new DirectoryInfo($"./Logs/{modName}/{DateTime.Now:dd-MM-yy}"); + logsDir.Create(); + return new FileInfo($"{logsDir.FullName}/{logsDir.GetFiles().Length}.txt"); + } + } + + public static class DebugExtensions { + public static Logger GetLogger(this IPlugin plugin) { + return new Logger(plugin); + } + } +} \ No newline at end of file diff --git a/IllusionPlugin/ModPrefs.cs b/IllusionPlugin/ModPrefs.cs index 9c55d37c..91f3daf2 100644 --- a/IllusionPlugin/ModPrefs.cs +++ b/IllusionPlugin/ModPrefs.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using System.Reflection; using System.Text; namespace IllusionPlugin @@ -8,21 +10,15 @@ namespace IllusionPlugin /// /// Allows to get and set preferences for your mod. /// - public static class ModPrefs - { - private static IniFile _instance; - private static IniFile Instance - { - get - { - if (_instance == null) - { - _instance = new IniFile(Path.Combine(Environment.CurrentDirectory, "UserData/modprefs.ini")); - } - return _instance; - } - } + public class ModPrefs { + internal static Dictionary ModPrefses { get; set; } = new Dictionary(); + private IniFile Instance; + + public ModPrefs(IPlugin plugin) { + Instance = new IniFile(Path.Combine(Environment.CurrentDirectory, $"UserData/ModPrefs/{plugin.Name}.ini")); + ModPrefses.Add(plugin, this); + } /// /// Gets a string from the ini. @@ -32,9 +28,9 @@ namespace IllusionPlugin /// Value that should be used when no value is found. /// Whether or not the default value should be written if no value is found. /// - public static string GetString(string section, string name, string defaultValue = "", bool autoSave = false) + public string GetString(string section, string name, string defaultValue = "", bool autoSave = false) { - string value = Instance.IniReadValue(section, name); + var value = Instance.IniReadValue(section, name); if (value != "") return value; else if (autoSave) @@ -51,10 +47,9 @@ namespace IllusionPlugin /// Value that should be used when no value is found. /// Whether or not the default value should be written if no value is found. /// - public static int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) + public int GetInt(string section, string name, int defaultValue = 0, bool autoSave = false) { - int value; - if (int.TryParse(Instance.IniReadValue(section, name), out value)) + if (int.TryParse(Instance.IniReadValue(section, name), out var value)) return value; else if (autoSave) SetInt(section, name, defaultValue); @@ -71,10 +66,9 @@ namespace IllusionPlugin /// Value that should be used when no value is found. /// Whether or not the default value should be written if no value is found. /// - public static float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) + public float GetFloat(string section, string name, float defaultValue = 0f, bool autoSave = false) { - float value; - if (float.TryParse(Instance.IniReadValue(section, name), out value)) + if (float.TryParse(Instance.IniReadValue(section, name), out var value)) return value; else if (autoSave) SetFloat(section, name, defaultValue); @@ -90,7 +84,7 @@ namespace IllusionPlugin /// Value that should be used when no value is found. /// Whether or not the default value should be written if no value is found. /// - public static bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) + public bool GetBool(string section, string name, bool defaultValue = false, bool autoSave = false) { string sVal = GetString(section, name, null); if (sVal == "1" || sVal == "0") @@ -111,7 +105,7 @@ namespace IllusionPlugin /// Section of the key. /// Name of the key. /// - public static bool HasKey(string section, string name) + public bool HasKey(string section, string name) { return Instance.IniReadValue(section, name) != null; } @@ -122,7 +116,7 @@ namespace IllusionPlugin /// Section of the key. /// Name of the key. /// Value that should be written. - public static void SetFloat(string section, string name, float value) + public void SetFloat(string section, string name, float value) { Instance.IniWriteValue(section, name, value.ToString()); } @@ -133,7 +127,7 @@ namespace IllusionPlugin /// Section of the key. /// Name of the key. /// Value that should be written. - public static void SetInt(string section, string name, int value) + public void SetInt(string section, string name, int value) { Instance.IniWriteValue(section, name, value.ToString()); @@ -145,7 +139,7 @@ namespace IllusionPlugin /// Section of the key. /// Name of the key. /// Value that should be written. - public static void SetString(string section, string name, string value) + public void SetString(string section, string name, string value) { Instance.IniWriteValue(section, name, value); @@ -157,10 +151,17 @@ namespace IllusionPlugin /// Section of the key. /// Name of the key. /// Value that should be written. - public static void SetBool(string section, string name, bool value) + public void SetBool(string section, string name, bool value) { Instance.IniWriteValue(section, name, value ? "1" : "0"); } + + } + + public static class ModPrefsExtensions { + public static ModPrefs GetModPrefs(this IPlugin plugin) { + return ModPrefs.ModPrefses.First(o => o.Key == plugin).Value; + } } }