1.4.5 (pre-release)

* Pre-release. Will be released once MelonLoader bumps to Unhollower 0.4.9.0
* Added global "Force Unlock Mouse" option, should work on almost all games. Has smart behaviour and will maintain the previous value (or the value which should be set).
* Improve performacne of CacheList casting List ->IEnumerable
* Fix a bug causing some Components to not show the GameObject button in the Reflection Window (top-right corner).
* Fix a bug making the Window Manager think that two of the same Il2Cpp Object are not ReferenceEquals.
* Added logging when C# Console fails to compile anything
* Improve display of Reflection Window member name label, now expands with window resize.
This commit is contained in:
sinaioutlander
2020-08-27 18:05:55 +10:00
parent e567c16221
commit 535e88be9a
10 changed files with 222 additions and 67 deletions

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using MelonLoader;
using Mono.CSharp;
using UnityEngine;
@ -22,7 +23,15 @@ namespace Explorer
{
if (m_entryType == null)
{
m_entryType = Value?.GetType().GetGenericArguments()[0];
switch (this.MemberInfoType)
{
case ReflectionWindow.MemberInfoType.Field:
m_entryType = (MemberInfo as FieldInfo).FieldType.GetGenericArguments()[0];
break;
case ReflectionWindow.MemberInfoType.Property:
m_entryType = (MemberInfo as PropertyInfo).PropertyType.GetGenericArguments()[0];
break;
}
}
return m_entryType;
}
@ -39,7 +48,7 @@ namespace Explorer
{
if (m_enumerable == null && Value != null)
{
m_enumerable = Value as IEnumerable ?? CppListToEnumerable(Value);
m_enumerable = Value as IEnumerable ?? CastValueFromList();
}
return m_enumerable;
}
@ -48,14 +57,23 @@ namespace Explorer
private IEnumerable m_enumerable;
private CacheObject[] m_cachedEntries;
private IEnumerable CppListToEnumerable(object list)
public MethodInfo GenericToArrayMethod
{
if (EntryType == null) return null;
get
{
if (EntryType == null) return null;
return (IEnumerable)typeof(Il2CppSystem.Collections.Generic.List<>)
.MakeGenericType(new Type[] { EntryType })
.GetMethod("ToArray")
.Invoke(list, new object[0]);
return m_genericToArray ??
(m_genericToArray = typeof(Il2CppSystem.Collections.Generic.List<>)
.MakeGenericType(new Type[] { this.EntryType })
.GetMethod("ToArray"));
}
}
private MethodInfo m_genericToArray;
private IEnumerable CastValueFromList()
{
return (Value == null) ? null : (IEnumerable)GenericToArrayMethod?.Invoke(Value, new object[0]);
}
public override void DrawValue(Rect window, float width)

View File

@ -132,8 +132,18 @@ namespace Explorer
return holder;
}
private const float MAX_WIDTH = 400f;
public void Draw(Rect window, float labelWidth = 215f)
{
if (labelWidth > 0)
{
float min = (window.width * 0.37f);
if (min > MAX_WIDTH) min = MAX_WIDTH;
labelWidth = Mathf.Clamp(labelWidth, min, MAX_WIDTH);
}
if (MemberInfo != null)
{
GUILayout.Label("<color=cyan>" + FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });

View File

@ -1,19 +1,18 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using System.Reflection;
using Harmony;
using MelonLoader;
using UnhollowerBaseLib;
using UnityEngine;
namespace Explorer
{
public class CppExplorer : MelonMod
{
// consts
public const string ID = "com.sinai.cppexplorer";
public const string VERSION = "1.4.2";
public const string GUID = "com.sinai.cppexplorer";
public const string VERSION = "1.4.5";
public const string AUTHOR = "Sinai";
public const string NAME = "CppExplorer"
@ -22,26 +21,52 @@ namespace Explorer
#endif
;
// fields
public static CppExplorer Instance { get; private set; }
public static CppExplorer Instance;
public static bool ShowMenu
{
get => m_showMenu;
set => SetShowMenu(value);
}
private static bool m_showMenu;
// props
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 ShowMenu { get; set; } = false;
public static bool ShouldForceMouse => ShowMenu && ForceUnlockMouse;
// methods
private static void SetShowMenu(bool show)
{
m_showMenu = show;
UpdateCursorControl();
}
// ========== MonoBehaviour methods ==========
public override void OnApplicationStart()
{
base.OnApplicationStart();
Instance = this;
new MainMenu();
new WindowManager();
ShowMenu = true;
// Get current cursor state and enable cursor
m_lastLockMode = Cursor.lockState;
m_lastVisibleState = Cursor.visible;
// Enable ShowMenu and ForceUnlockMouse
// (set m_showMenu to not call UpdateCursorState twice)
m_showMenu = true;
SetForceUnlock(true);
MelonLogger.Log($"CppExplorer {VERSION} initialized.");
}
public override void OnLevelWasLoaded(int level)
@ -52,6 +77,7 @@ namespace Explorer
public override void OnUpdate()
{
// Check main toggle key input
if (Input.GetKeyDown(KeyCode.F7))
{
ShowMenu = !ShowMenu;
@ -59,15 +85,14 @@ namespace Explorer
if (ShowMenu)
{
if (!Cursor.visible)
// Check Force-Unlock input
if (Input.GetKeyDown(KeyCode.LeftAlt))
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
ForceUnlockMouse = !ForceUnlockMouse;
}
MainMenu.Instance.Update();
WindowManager.Instance.Update();
InspectUnderMouse.Update();
}
}
@ -78,8 +103,99 @@ namespace Explorer
MainMenu.Instance.OnGUI();
WindowManager.Instance.OnGUI();
InspectUnderMouse.OnGUI();
}
// =========== Cursor control ===========
private static void SetForceUnlock(bool unlock)
{
m_forceUnlock = unlock;
UpdateCursorControl();
}
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_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.visible), MethodType.Getter)]
public class Cursor_get_visible
{
[HarmonyPostfix]
public static void Postfix(ref bool __result)
{
if (ShouldForceMouse)
{
__result = m_lastVisibleState;
}
}
}
[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;
}
}
}
}
}

View File

@ -42,10 +42,6 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2Cppmscorlib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2CppSystem.Core">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mcs">
<HintPath>..\lib\mcs.dll</HintPath>
<Private>False</Private>
@ -64,10 +60,6 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnhollowerRuntimeLib">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnhollowerRuntimeLib.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- Unity 2019 build (InputLegacyModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.dll</HintPath>
@ -97,10 +89,6 @@
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIElementsModule" Condition="'$(Configuration)'=='Debug'">
<HintPath>..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
<!-- Unity 2018 build (InputModule.dll) -->
<Reference Include="UnityEngine" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.dll</HintPath>
@ -130,10 +118,6 @@
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UI.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.UIElementsModule" Condition="'$(Configuration)'=='Release_Unity2018'">
<HintPath>..\..\..\Steam\steamapps\common\VRChat\MelonLoader\Managed\UnityEngine.UIElementsModule.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CachedObjects\CacheEnum.cs" />

View File

@ -8,15 +8,12 @@ using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using BF = System.Reflection.BindingFlags;
using ILBF = Il2CppSystem.Reflection.BindingFlags;
using MelonLoader;
namespace Explorer
{
public class ReflectionHelpers
{
public static BF CommonFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
public static ILBF CommonFlags_IL = ILBF.Public | ILBF.NonPublic | ILBF.Instance | ILBF.Static;
public static Il2CppSystem.Type GameObjectType => Il2CppType.Of<GameObject>();
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();

View File

@ -103,9 +103,15 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
bool mouseState = CppExplorer.ForceUnlockMouse;
bool setMouse = GUILayout.Toggle(mouseState, "Force Unlock Mouse (Left Alt)", null);
if (setMouse != mouseState) CppExplorer.ForceUnlockMouse = setMouse;
GUILayout.EndHorizontal();
GUILayout.Space(10);
GUI.color = Color.white;
}

View File

@ -86,11 +86,11 @@ MelonLogger.Log(""hello world"");";
if (!UsingDirectives.Contains(asm))
{
UsingDirectives.Add(asm);
Evaluate(AsmToUsing(asm));
Evaluate(AsmToUsing(asm), true);
}
}
public object Evaluate(string str)
public object Evaluate(string str, bool suppressWarning = false)
{
object ret = VoidType.Value;
@ -98,11 +98,19 @@ MelonLogger.Log(""hello world"");";
try
{
compiled?.Invoke(ref ret);
if (compiled == null)
{
throw new Exception("Mono.Csharp Service was unable to compile the code provided.");
}
compiled.Invoke(ref ret);
}
catch (Exception e)
{
MelonLogger.LogWarning(e.ToString());
if (!suppressWarning)
{
MelonLogger.LogWarning(e.GetType() + ", " + e.Message);
}
}
return ret;
@ -114,7 +122,7 @@ MelonLogger.Log(""hello world"");";
GUILayout.Label("<b><size=15><color=cyan>C# REPL Console</color></size></b>", null);
GUILayout.Label("Method:", null);
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) });
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(250) });
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
{
@ -144,7 +152,7 @@ MelonLogger.Log(""hello world"");";
GUILayout.Label(AsmToUsing(asm, true), null);
}
GUILayout.BeginHorizontal(null);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(105) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("<b><color=lime>Add</color></b>", new GUILayoutOption[] { GUILayout.Width(120) }))
{

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using MelonLoader;
using Mono.CSharp;
using UnhollowerBaseLib;
using UnityEngine;
@ -190,7 +189,9 @@ namespace Explorer
UIHelpers.InstantiateButton((UnityEngine.Object)Target);
if (Target is Component comp && comp.gameObject is GameObject obj)
var comp = (Target as Il2CppSystem.Object).TryCast<Component>();
if (comp && comp.gameObject is GameObject obj)
{
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("GameObject:", null);

View File

@ -102,12 +102,24 @@ namespace Explorer
{
createdNew = false;
UnityEngine.Object uObj = null;
if (obj is UnityEngine.Object)
{
uObj = obj as UnityEngine.Object;
}
foreach (var window in Windows)
{
if (ReferenceEquals(obj, window.Target))
bool equals;
equals = ReferenceEquals(obj, window.Target);
if (!equals && uObj != null && window.Target is UnityEngine.Object uTarget)
{
GUI.BringWindowToFront(window.windowID);
GUI.FocusWindow(window.windowID);
equals = uObj.m_CachedPtr == uTarget.m_CachedPtr;
}
if (equals)
{
return window;
}
}
@ -181,7 +193,10 @@ namespace Explorer
GUILayout.EndHorizontal();
}
catch { }
catch //(Exception e)
{
//MelonLogger.Log("Exception on GuiResize: " + e.GetType() + ", " + e.Message);
}
return _rect;
}