mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-03 03:52:28 +08:00
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:
@ -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)
|
||||
|
@ -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) });
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) }))
|
||||
{
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user