From 8d648fec43f46909ae8709913141291658fc0a82 Mon Sep 17 00:00:00 2001 From: sinaioutlander <49360850+sinaioutlander@users.noreply.github.com> Date: Fri, 11 Sep 2020 18:53:17 +1000 Subject: [PATCH] 1.6.9 * Fix for games where patching Cursor methods fails. * Added backup attempt for loading Cursor module if not present. * HashSet collections are now supported by CacheList * try/catch for loading Mod Config --- src/CppExplorer.cs | 132 ++-------- src/CppExplorer.csproj | 31 +-- src/Helpers/InputHelper.cs | 20 +- src/Helpers/ReflectionHelpers.cs | 19 ++ src/Menu/CursorControl.cs | 238 ++++++++++++++++++ src/{ => Menu}/MainMenu/InspectUnderMouse.cs | 0 src/{ => Menu}/MainMenu/MainMenu.cs | 9 +- src/{ => Menu}/MainMenu/Pages/Console/REPL.cs | 0 .../MainMenu/Pages/Console/REPLHelper.cs | 0 src/{ => Menu}/MainMenu/Pages/ConsolePage.cs | 0 src/{ => Menu}/MainMenu/Pages/ScenePage.cs | 0 src/{ => Menu}/MainMenu/Pages/SearchPage.cs | 0 src/{ => Menu}/MainMenu/Pages/WindowPage.cs | 0 src/{Helpers => Menu}/UIStyles.cs | 0 src/{ => Menu}/Windows/GameObjectWindow.cs | 0 src/{ => Menu}/Windows/ReflectionWindow.cs | 0 src/{ => Menu}/Windows/ResizeDrag.cs | 0 src/{ => Menu}/Windows/TabViewWindow.cs | 0 src/{ => Menu}/Windows/UIWindow.cs | 10 +- src/{ => Menu}/Windows/WindowManager.cs | 0 20 files changed, 292 insertions(+), 167 deletions(-) create mode 100644 src/Menu/CursorControl.cs rename src/{ => Menu}/MainMenu/InspectUnderMouse.cs (100%) rename src/{ => Menu}/MainMenu/MainMenu.cs (92%) rename src/{ => Menu}/MainMenu/Pages/Console/REPL.cs (100%) rename src/{ => Menu}/MainMenu/Pages/Console/REPLHelper.cs (100%) rename src/{ => Menu}/MainMenu/Pages/ConsolePage.cs (100%) rename src/{ => Menu}/MainMenu/Pages/ScenePage.cs (100%) rename src/{ => Menu}/MainMenu/Pages/SearchPage.cs (100%) rename src/{ => Menu}/MainMenu/Pages/WindowPage.cs (100%) rename src/{Helpers => Menu}/UIStyles.cs (100%) rename src/{ => Menu}/Windows/GameObjectWindow.cs (100%) rename src/{ => Menu}/Windows/ReflectionWindow.cs (100%) rename src/{ => Menu}/Windows/ResizeDrag.cs (100%) rename src/{ => Menu}/Windows/TabViewWindow.cs (100%) rename src/{ => Menu}/Windows/UIWindow.cs (85%) rename src/{ => Menu}/Windows/WindowManager.cs (100%) diff --git a/src/CppExplorer.cs b/src/CppExplorer.cs index e69e20d..a6694ad 100644 --- a/src/CppExplorer.cs +++ b/src/CppExplorer.cs @@ -13,7 +13,7 @@ namespace Explorer public class CppExplorer : MelonMod { public const string NAME = "CppExplorer"; - public const string VERSION = "1.6.8"; + public const string VERSION = "1.6.9"; public const string AUTHOR = "Sinai"; public const string GUID = "com.sinai.cppexplorer"; @@ -24,51 +24,30 @@ namespace Explorer get => m_showMenu; set => SetShowMenu(value); } - private static bool m_showMenu; - - public static bool ForceUnlockMouse - { - get => m_forceUnlock; - set => SetForceUnlock(value); - } - private static bool m_forceUnlock; - private static CursorLockMode m_lastLockMode; - private static bool m_lastVisibleState; - private static bool m_currentlySettingCursor = false; - - public static bool ShouldForceMouse => ShowMenu && ForceUnlockMouse; + public static bool m_showMenu; private static void SetShowMenu(bool show) { m_showMenu = show; - UpdateCursorControl(); - } - - private static void SetForceUnlock(bool unlock) - { - m_forceUnlock = unlock; - UpdateCursorControl(); + CursorControl.UpdateCursorControl(); } public override void OnApplicationStart() { Instance = this; + // First, load config ModConfig.OnLoad(); + // Setup InputHelper class (UnityEngine.Input) InputHelper.Init(); + // Create CppExplorer modules new MainMenu(); new WindowManager(); - // Get current cursor state and enable cursor - m_lastLockMode = Cursor.lockState; - m_lastVisibleState = Cursor.visible; - - // Enable ShowMenu and ForceUnlockMouse - // (set m_showMenu directly to not call UpdateCursorState twice) - m_showMenu = true; - ForceUnlockMouse = true; + // Init cursor control + CursorControl.Init(); MelonLogger.Log($"CppExplorer {VERSION} initialized."); } @@ -89,15 +68,11 @@ namespace Explorer if (ShowMenu) { - // Check Force-Unlock input - if (InputHelper.GetKeyDown(KeyCode.LeftAlt)) - { - ForceUnlockMouse = !ForceUnlockMouse; - } + CursorControl.Update(); + InspectUnderMouse.Update(); MainMenu.Instance.Update(); WindowManager.Instance.Update(); - InspectUnderMouse.Update(); } } @@ -105,93 +80,14 @@ namespace Explorer { if (!ShowMenu) return; + var origSkin = GUI.skin; + GUI.skin = UIStyles.WindowSkin; + MainMenu.Instance.OnGUI(); WindowManager.Instance.OnGUI(); InspectUnderMouse.OnGUI(); - } - private static void UpdateCursorControl() - { - m_currentlySettingCursor = true; - if (ShouldForceMouse) - { - Cursor.lockState = CursorLockMode.None; - Cursor.visible = true; - } - else - { - Cursor.lockState = m_lastLockMode; - Cursor.visible = m_lastVisibleState; - } - m_currentlySettingCursor = false; - } - - // Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true. - // Also keep track of when anything else tries to set Cursor state, this will be the - // value that we set back to when we close the menu or disable force-unlock. - - [HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)] - public class Cursor_set_lockState - { - [HarmonyPrefix] - public static void Prefix(ref CursorLockMode value) - { - if (!m_currentlySettingCursor) - { - m_lastLockMode = value; - - if (ShouldForceMouse) - { - value = CursorLockMode.None; - } - } - } - } - - [HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Setter)] - public class Cursor_set_visible - { - [HarmonyPrefix] - public static void Prefix(ref bool value) - { - if (!m_currentlySettingCursor) - { - m_lastVisibleState = value; - - if (ShouldForceMouse) - { - value = true; - } - } - } - } - - // Make it appear as though UnlockMouse is disabled to the rest of the application. - - [HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)] - public class Cursor_get_lockState - { - [HarmonyPostfix] - public static void Postfix(ref CursorLockMode __result) - { - if (ShouldForceMouse) - { - __result = m_lastLockMode; - } - } - } - - [HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)] - public class Cursor_get_visible - { - [HarmonyPostfix] - public static void Postfix(ref bool __result) - { - if (ShouldForceMouse) - { - __result = m_lastVisibleState; - } - } + GUI.skin = origSkin; } } } diff --git a/src/CppExplorer.csproj b/src/CppExplorer.csproj index 6a568db..467c78e 100644 --- a/src/CppExplorer.csproj +++ b/src/CppExplorer.csproj @@ -93,6 +93,7 @@ + @@ -101,24 +102,24 @@ - + - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Helpers/InputHelper.cs b/src/Helpers/InputHelper.cs index c38e43c..ca0ebef 100644 --- a/src/Helpers/InputHelper.cs +++ b/src/Helpers/InputHelper.cs @@ -88,7 +88,8 @@ namespace Explorer { MelonLogger.Log("UnityEngine.Input is null, trying to load manually...."); - if ((TryLoad("UnityEngine.InputLegacyModule.dll") || TryLoad("UnityEngine.CoreModule.dll")) && Input != null) + if ((ReflectionHelpers.LoadModule("UnityEngine.InputLegacyModule.dll") || ReflectionHelpers.LoadModule("UnityEngine.CoreModule.dll")) + && Input != null) { MelonLogger.Log("Ok!"); return true; @@ -98,23 +99,6 @@ namespace Explorer MelonLogger.Log("Could not load Input module!"); return false; } - - bool TryLoad(string module) - { - var path = $@"MelonLoader\Managed\{module}"; - if (!File.Exists(path)) return false; - - try - { - Assembly.Load(File.ReadAllBytes(path)); - return true; - } - catch (Exception e) - { - MelonLogger.Log(e.GetType() + ", " + e.Message); - return false; - } - } } } } diff --git a/src/Helpers/ReflectionHelpers.cs b/src/Helpers/ReflectionHelpers.cs index afc24c5..80bd93b 100644 --- a/src/Helpers/ReflectionHelpers.cs +++ b/src/Helpers/ReflectionHelpers.cs @@ -3,6 +3,8 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; +using System.IO; +using MelonLoader; using UnhollowerBaseLib; using UnhollowerRuntimeLib; using UnityEngine; @@ -122,6 +124,23 @@ namespace Explorer return list.ToArray(); } + public static bool LoadModule(string module) + { + var path = $@"MelonLoader\Managed\{module}"; + if (!File.Exists(path)) return false; + + try + { + Assembly.Load(File.ReadAllBytes(path)); + return true; + } + catch (Exception e) + { + MelonLogger.Log(e.GetType() + ", " + e.Message); + return false; + } + } + public static string ExceptionToString(Exception e) { if (IsFailedGeneric(e)) diff --git a/src/Menu/CursorControl.cs b/src/Menu/CursorControl.cs new file mode 100644 index 0000000..9f255aa --- /dev/null +++ b/src/Menu/CursorControl.cs @@ -0,0 +1,238 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Harmony; +using MelonLoader; +using System.Reflection; + +namespace Explorer +{ + public class CursorControl + { + public static bool ForceUnlockMouse + { + get => m_forceUnlock; + set => SetForceUnlock(value); + } + private static bool m_forceUnlock; + private static CursorLockMode m_lastLockMode; + private static bool m_lastVisibleState; + private static bool m_currentlySettingCursor = false; + + public static bool ShouldForceMouse => CppExplorer.ShowMenu && ForceUnlockMouse; + + public static void Init() + { + try + { + // Check if Cursor class is loaded + if (ReflectionHelpers.GetTypeByName("UnityEngine.Cursor") == null) + { + MelonLogger.Log("Trying to manually load Cursor module..."); + + if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule")) + { + MelonLogger.Log("Ok!"); + } + else + { + throw new Exception("Could not load UnityEngine.Cursor module!"); + } + } + + // Get current cursor state and enable cursor + m_lastLockMode = Cursor.lockState; + m_lastVisibleState = Cursor.visible; + + TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_lockState))), false, false); + TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Prefix_set_visible))), false, false); + + TryPatch("lockState", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_lockState))), true, true); + TryPatch("visible", new HarmonyMethod(typeof(CursorControl).GetMethod(nameof(Postfix_get_visible))), true, true); + } + catch (Exception e) + { + MelonLogger.Log($"Exception on CursorControl.Init! {e.GetType()}, {e.Message}"); + } + + // Enable ShowMenu and ForceUnlockMouse + // (set m_showMenu directly to not call UpdateCursorState twice) + CppExplorer.m_showMenu = true; + ForceUnlockMouse = true; + } + + private static void TryPatch(string property, HarmonyMethod patch, bool getter = true, bool postfix = false) + { + // Setup Harmony Patches + try + { + var harmony = CppExplorer.Instance.harmonyInstance; + + var prop = typeof(Cursor).GetProperty(property); + + harmony.Patch(getter ? prop.GetGetMethod() : prop.GetSetMethod(), + postfix ? null : patch, + postfix ? patch : null); + } + catch (Exception e) + { + MelonLogger.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}"); + } + } + + private static void SetForceUnlock(bool unlock) + { + m_forceUnlock = unlock; + UpdateCursorControl(); + } + + public static void Update() + { + // Check Force-Unlock input + if (InputHelper.GetKeyDown(KeyCode.LeftAlt)) + { + ForceUnlockMouse = !ForceUnlockMouse; + } + } + + public static void UpdateCursorControl() + { + try + { + m_currentlySettingCursor = true; + if (ShouldForceMouse) + { + Cursor.lockState = CursorLockMode.None; + Cursor.visible = true; + } + else + { + Cursor.lockState = m_lastLockMode; + Cursor.visible = m_lastVisibleState; + } + m_currentlySettingCursor = false; + } + catch (Exception e) + { + MelonLogger.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}"); + } + } + + // Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true. + // Also keep track of when anything else tries to set Cursor state, this will be the + // value that we set back to when we close the menu or disable force-unlock. + + [HarmonyPrefix] + public static void Prefix_set_lockState(ref CursorLockMode value) + { + if (!m_currentlySettingCursor) + { + m_lastLockMode = value; + + if (ShouldForceMouse) + { + value = CursorLockMode.None; + } + } + } + + [HarmonyPrefix] + public static void Prefix_set_visible(ref bool value) + { + if (!m_currentlySettingCursor) + { + m_lastVisibleState = value; + + if (ShouldForceMouse) + { + value = true; + } + } + } + + [HarmonyPrefix] + public static void Postfix_get_lockState(ref CursorLockMode __result) + { + if (ShouldForceMouse) + { + __result = m_lastLockMode; + } + } + + [HarmonyPrefix] + public static void Postfix_get_visible(ref bool __result) + { + if (ShouldForceMouse) + { + __result = m_lastVisibleState; + } + } + + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Setter)] + //public class Cursor_set_lockState + //{ + // [HarmonyPrefix] + // public static void Prefix(ref CursorLockMode value) + // { + // if (!m_currentlySettingCursor) + // { + // m_lastLockMode = value; + + // if (ShouldForceMouse) + // { + // value = CursorLockMode.None; + // } + // } + // } + //} + + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Setter)] + //public class Cursor_set_visible + //{ + // [HarmonyPrefix] + // public static void Prefix(ref bool value) + // { + // if (!m_currentlySettingCursor) + // { + // m_lastVisibleState = value; + + // if (ShouldForceMouse) + // { + // value = true; + // } + // } + // } + //} + + //// Make it appear as though UnlockMouse is disabled to the rest of the application. + + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.lockState), MethodType.Getter)] + //public class Cursor_get_lockState + //{ + // [HarmonyPostfix] + // public static void Postfix(ref CursorLockMode __result) + // { + // if (ShouldForceMouse) + // { + // __result = m_lastLockMode; + // } + // } + //} + + //[HarmonyPatch(typeof(Cursor), nameof(Cursor.visible), MethodType.Getter)] + //public class Cursor_get_visible + //{ + // [HarmonyPostfix] + // public static void Postfix(ref bool __result) + // { + // if (ShouldForceMouse) + // { + // __result = m_lastVisibleState; + // } + // } + //} + } +} diff --git a/src/MainMenu/InspectUnderMouse.cs b/src/Menu/MainMenu/InspectUnderMouse.cs similarity index 100% rename from src/MainMenu/InspectUnderMouse.cs rename to src/Menu/MainMenu/InspectUnderMouse.cs diff --git a/src/MainMenu/MainMenu.cs b/src/Menu/MainMenu/MainMenu.cs similarity index 92% rename from src/MainMenu/MainMenu.cs rename to src/Menu/MainMenu/MainMenu.cs index 5784392..447f436 100644 --- a/src/MainMenu/MainMenu.cs +++ b/src/Menu/MainMenu/MainMenu.cs @@ -52,12 +52,7 @@ namespace Explorer public void OnGUI() { - var origSkin = GUI.skin; - GUI.skin = UIStyles.WindowSkin; - MainRect = GUI.Window(MainWindowID, MainRect, (GUI.WindowFunction)MainWindow, CppExplorer.NAME); - - GUI.skin = origSkin; } private void MainWindow(int id) @@ -108,9 +103,9 @@ namespace Explorer GUI.color = Color.white; InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null); - bool mouseState = CppExplorer.ForceUnlockMouse; + bool mouseState = CursorControl.ForceUnlockMouse; bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null); - if (setMouse != mouseState) CppExplorer.ForceUnlockMouse = setMouse; + if (setMouse != mouseState) CursorControl.ForceUnlockMouse = setMouse; WindowManager.TabView = GUILayout.Toggle(WindowManager.TabView, "Tab View", null); GUILayout.EndHorizontal(); diff --git a/src/MainMenu/Pages/Console/REPL.cs b/src/Menu/MainMenu/Pages/Console/REPL.cs similarity index 100% rename from src/MainMenu/Pages/Console/REPL.cs rename to src/Menu/MainMenu/Pages/Console/REPL.cs diff --git a/src/MainMenu/Pages/Console/REPLHelper.cs b/src/Menu/MainMenu/Pages/Console/REPLHelper.cs similarity index 100% rename from src/MainMenu/Pages/Console/REPLHelper.cs rename to src/Menu/MainMenu/Pages/Console/REPLHelper.cs diff --git a/src/MainMenu/Pages/ConsolePage.cs b/src/Menu/MainMenu/Pages/ConsolePage.cs similarity index 100% rename from src/MainMenu/Pages/ConsolePage.cs rename to src/Menu/MainMenu/Pages/ConsolePage.cs diff --git a/src/MainMenu/Pages/ScenePage.cs b/src/Menu/MainMenu/Pages/ScenePage.cs similarity index 100% rename from src/MainMenu/Pages/ScenePage.cs rename to src/Menu/MainMenu/Pages/ScenePage.cs diff --git a/src/MainMenu/Pages/SearchPage.cs b/src/Menu/MainMenu/Pages/SearchPage.cs similarity index 100% rename from src/MainMenu/Pages/SearchPage.cs rename to src/Menu/MainMenu/Pages/SearchPage.cs diff --git a/src/MainMenu/Pages/WindowPage.cs b/src/Menu/MainMenu/Pages/WindowPage.cs similarity index 100% rename from src/MainMenu/Pages/WindowPage.cs rename to src/Menu/MainMenu/Pages/WindowPage.cs diff --git a/src/Helpers/UIStyles.cs b/src/Menu/UIStyles.cs similarity index 100% rename from src/Helpers/UIStyles.cs rename to src/Menu/UIStyles.cs diff --git a/src/Windows/GameObjectWindow.cs b/src/Menu/Windows/GameObjectWindow.cs similarity index 100% rename from src/Windows/GameObjectWindow.cs rename to src/Menu/Windows/GameObjectWindow.cs diff --git a/src/Windows/ReflectionWindow.cs b/src/Menu/Windows/ReflectionWindow.cs similarity index 100% rename from src/Windows/ReflectionWindow.cs rename to src/Menu/Windows/ReflectionWindow.cs diff --git a/src/Windows/ResizeDrag.cs b/src/Menu/Windows/ResizeDrag.cs similarity index 100% rename from src/Windows/ResizeDrag.cs rename to src/Menu/Windows/ResizeDrag.cs diff --git a/src/Windows/TabViewWindow.cs b/src/Menu/Windows/TabViewWindow.cs similarity index 100% rename from src/Windows/TabViewWindow.cs rename to src/Menu/Windows/TabViewWindow.cs diff --git a/src/Windows/UIWindow.cs b/src/Menu/Windows/UIWindow.cs similarity index 85% rename from src/Windows/UIWindow.cs rename to src/Menu/Windows/UIWindow.cs index 31d2af8..1e0ae55 100644 --- a/src/Windows/UIWindow.cs +++ b/src/Menu/Windows/UIWindow.cs @@ -49,15 +49,7 @@ namespace Explorer public void OnGUI() { - if (CppExplorer.ShowMenu) - { - var origSkin = GUI.skin; - - GUI.skin = UIStyles.WindowSkin; - m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title); - - GUI.skin = origSkin; - } + m_rect = GUI.Window(windowID, m_rect, (GUI.WindowFunction)WindowFunction, Title); } public void Header() diff --git a/src/Windows/WindowManager.cs b/src/Menu/Windows/WindowManager.cs similarity index 100% rename from src/Windows/WindowManager.cs rename to src/Menu/Windows/WindowManager.cs