Compare commits

..

3 Commits
2.0.3 ... 2.0.4

Author SHA1 Message Date
39d9585f1d 2.0.4
* Added ability to see and change the layer of a gameobject from the GameObject inspector more easily, and shows you the actual layer name (where possible).
* Fixed an issue related to the recently-added clickthrough prevention and resize drag
* Fixed write-only properties in the inspector
* A few other minor fixes
2020-10-11 22:57:46 +11:00
2d414e544b Update README.md 2020-10-11 20:52:08 +11:00
513fcaa534 Universal click-through prevention attempt 2020-10-11 20:49:14 +11:00
15 changed files with 236 additions and 120 deletions

View File

@ -78,11 +78,16 @@ There is a simple Mod Config for the Explorer. You can access the settings via t
* Whether or not to show the Bitwise Editing helper when inspecting integers
`Enable Tab View` (bool) | Default: `true`
* Whether or not all inspector windows a grouped into a single window with tabs.
* Whether or not all inspector windows a grouped into a single window with tabs.
`Default Output Path` (string) | Default: `Mods\Explorer`
* Where output is generated to, by default (for Texture PNG saving, etc).
## Mouse Control
Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). However, you may also want to prevent the mouse clicking-through onto the game behind Explorer, this is possible but it requires specific patches for that game.
Explorer can force the mouse to be visible and unlocked when the menu is open, if you have enabled "Force Unlock Mouse" (Left-Alt toggle). Explorer also attempts to prevent clicking-through onto the game behind the Explorer menu.
If you need more mouse control:
* For VRChat, use [VRCExplorerMouseControl](https://github.com/sinai-dev/VRCExplorerMouseControl)
* For Hellpoint, use [HPExplorerMouseControl](https://github.com/sinai-dev/Hellpoint-Mods/tree/master/HPExplorerMouseControl/HPExplorerMouseControl)
@ -93,8 +98,9 @@ For example:
using Explorer;
using Harmony; // or 'using HarmonyLib;' for BepInEx
// ...
[HarmonyPatch(typeof(MyGame.MenuClass), nameof(MyGame.MenuClass.CursorUpdate)]
public class MenuClass_CursorUpdate
// You will need to figure out the relevant Class and Method for your game using dnSpy.
[HarmonyPatch(typeof(MyGame.InputManager), nameof(MyGame.InputManager.Update)]
public class InputManager_Update
{
[HarmonyPrefix]
public static bool Prefix()

View File

@ -35,11 +35,26 @@ namespace Explorer.CacheObject
try
{
var pi = MemInfo as PropertyInfo;
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
IValue.Value = pi.GetValue(target, ParseArguments());
if (pi.CanRead)
{
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
base.UpdateValue();
IValue.Value = pi.GetValue(target, ParseArguments());
base.UpdateValue();
}
else // create a dummy value for Write-Only properties.
{
if (IValue.ValueType == typeof(string))
{
IValue.Value = "";
}
else
{
IValue.Value = Activator.CreateInstance(IValue.ValueType);
}
}
}
catch (Exception e)
{

View File

@ -262,6 +262,7 @@
<Compile Include="UI\WindowBase.cs" />
<Compile Include="UI\WindowManager.cs" />
<Compile Include="Unstrip\ImageConversion\ImageConversionUnstrip.cs" />
<Compile Include="Unstrip\LayerMask\LayerMaskUnstrip.cs" />
<Compile Include="Unstrip\Scene\SceneUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\GUIUnstrip.cs" />
<Compile Include="Unstrip\IMGUI\Internal_LayoutUtility.cs" />

View File

@ -10,7 +10,7 @@ namespace Explorer
public class ExplorerCore
{
public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")";
public const string VERSION = "2.0.3";
public const string VERSION = "2.0.4";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.explorer";
@ -31,6 +31,12 @@ namespace Explorer
public ExplorerCore()
{
if (Instance != null)
{
Log("An instance of Explorer is already active!");
return;
}
Instance = this;
ModConfig.OnLoad();
@ -87,6 +93,11 @@ namespace Explorer
WindowManager.Instance.OnGUI();
InspectUnderMouse.OnGUI();
if (!ResizeDrag.IsMouseInResizeArea && WindowManager.IsMouseInWindow)
{
InputManager.ResetInputAxes();
}
GUI.skin = origSkin;
}

View File

@ -42,6 +42,16 @@ namespace Explorer
public static bool GetMouseButtonDown(int btn) => inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => inputModule.GetMouseButton(btn);
#if CPP
internal delegate void d_ResetInputAxes();
internal static d_ResetInputAxes ResetInputAxes_iCall =
IL2CPP.ResolveICall<d_ResetInputAxes>("UnityEngine.Input::ResetInputAxes");
public static void ResetInputAxes() => ResetInputAxes_iCall();
#else
public static void ResetInputAxes() => UnityEngine.Input.ResetInputAxes();
#endif
//#if CPP
//#pragma warning disable IDE1006
// // public extern static string compositionString { get; }

View File

@ -15,16 +15,13 @@ namespace Explorer.Tests
public static class StaticTestClass
{
public static int StaticProperty => 5;
public static int StaticField = 69;
public static List<string> StaticList = new List<string>
{
"one",
"two",
"three",
};
public static void StaticMethod() { }
}
@ -34,6 +31,14 @@ namespace Explorer.Tests
public static TestClass Instance => m_instance ?? (m_instance = new TestClass());
private static TestClass m_instance;
public static bool ReadSetOnlyProperty => m_setOnlyProperty;
public static bool SetOnlyProperty
{
set => m_setOnlyProperty = value;
}
private static bool m_setOnlyProperty;
public Texture2D TestTexture = UIStyles.MakeTex(200, 200, Color.white);
public static Sprite TestSprite;

View File

@ -17,32 +17,25 @@ namespace Explorer.UI
}
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 => ExplorerCore.ShowMenu && Unlock;
private static Type CursorType => m_cursorType ?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false;
private static Type CursorType
=> m_cursorType
?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type m_cursorType;
public static void Init()
{
try
{
// Check if Cursor class is loaded
if (CursorType == null)
{
ExplorerCore.Log("Trying to manually load Cursor module...");
if (ReflectionHelpers.LoadModule("UnityEngine.CoreModule") && CursorType != null)
{
ExplorerCore.Log("Ok!");
}
else
{
throw new Exception("Could not load UnityEngine.Cursor module!");
}
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
}
// Get current cursor state and enable cursor
@ -91,7 +84,8 @@ namespace Explorer.UI
}
catch (Exception e)
{
ExplorerCore.Log($"[NON-FATAL] Couldn't patch a method: {e.Message}");
string s = setter ? "set_" : "get_" ;
ExplorerCore.Log($"Unable to patch Cursor.{s}{property}: {e.Message}");
}
}

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using UnityEngine;
using Explorer.UI.Shared;
using Explorer.UI.Main;
using Explorer.Unstrip.LayerMasks;
#if CPP
using UnhollowerRuntimeLib;
#endif
@ -17,6 +18,8 @@ namespace Explorer.UI.Inspectors
public GameObject TargetGO;
public bool pendingDestroy;
private static bool m_hideControls;
// gui element holders
@ -43,6 +46,8 @@ namespace Explorer.UI.Inspectors
private bool m_autoUpdateTransform;
private bool m_localContext;
private int m_layer;
private readonly List<Component> m_cachedDestroyList = new List<Component>();
private string m_addComponentInput = "";
@ -104,16 +109,16 @@ namespace Explorer.UI.Inspectors
{
try
{
if (pendingDestroy) return;
if (Target == null)
{
ExplorerCore.Log("Target is null!");
DestroyWindow();
DestroyOnException(new Exception("Target was destroyed."));
return;
}
if (!TargetGO && !GetObjectAsGameObject())
{
ExplorerCore.Log("Target was destroyed!");
DestroyWindow();
DestroyOnException(new Exception("Target was destroyed."));
return;
}
@ -132,6 +137,8 @@ namespace Explorer.UI.Inspectors
TargetGO.transform.localScale = m_frozenScale;
}
m_layer = TargetGO.layer;
// update child objects
var childList = new List<Transform>();
for (int i = 0; i < TargetGO.transform.childCount; i++)
@ -163,6 +170,7 @@ namespace Explorer.UI.Inspectors
private void DestroyOnException(Exception e)
{
ExplorerCore.Log($"Exception drawing GameObject Window: {e.GetType()}, {e.Message}");
pendingDestroy = true;
DestroyWindow();
}
@ -204,6 +212,8 @@ namespace Explorer.UI.Inspectors
public override void WindowFunction(int windowID)
{
if (pendingDestroy) return;
try
{
var rect = WindowManager.TabView ? TabViewWindow.Instance.m_rect : this.m_rect;
@ -250,6 +260,8 @@ namespace Explorer.UI.Inspectors
GUIUnstrip.TextArea(m_name, new GUILayoutOption[0]);
GUILayout.EndHorizontal();
LayerControls();
// --- Horizontal Columns section ---
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
@ -280,6 +292,34 @@ namespace Explorer.UI.Inspectors
}
}
private void LayerControls()
{
GUIUnstrip.BeginHorizontal();
GUILayout.Label("Layer:", new GUILayoutOption[] { GUILayout.Width(50) });
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
{
if (m_layer > 0)
{
m_layer--;
if (TargetGO) TargetGO.layer = m_layer;
}
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
if (m_layer < 32)
{
m_layer++;
if (TargetGO) TargetGO.layer = m_layer;
}
}
GUILayout.Label($"{m_layer} (<color=cyan>{LayerMaskUnstrip.LayerToName(m_layer)}</color>)",
new GUILayoutOption[] { GUILayout.Width(200) });
GUILayout.EndHorizontal();
}
private void TransformList(Rect m_rect)
{
GUIUnstrip.BeginVertical(GUIContent.none, GUI.skin.box, null);

View File

@ -80,12 +80,13 @@ namespace Explorer.UI
if (OwnerCacheObject.CanWrite)
{
b = GUILayout.Toggle(b, label, new GUILayoutOption[0]);
if (b != (bool)Value)
{
Value = b;
OwnerCacheObject.SetValue();
}
Value = GUILayout.Toggle(b, label, new GUILayoutOption[] { GUILayout.Width(60) });
DrawApplyButton();
//if (b != (bool)Value)
//{
// Value = b;
// OwnerCacheObject.SetValue();
//}
}
else
{
@ -104,13 +105,8 @@ namespace Explorer.UI
GUILayout.Label("<color=#2df7b2><i>" + ValueType.Name + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
m_valueToString = GUIUnstrip.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.ExpandWidth(true) });
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
SetValueFromInput();
}
}
DrawApplyButton();
if (ModConfig.Instance.Bitwise_Support && m_canBitwiseOperate)
{
@ -129,6 +125,24 @@ namespace Explorer.UI
GUILayout.EndVertical();
}
private void DrawApplyButton()
{
if (OwnerCacheObject.CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
if (m_isBool)
{
OwnerCacheObject.SetValue();
}
else
{
SetValueFromInput();
}
}
}
}
private void DrawBitwise()
{
if (OwnerCacheObject.CanWrite)

View File

@ -63,26 +63,36 @@ namespace Explorer.UI.Main
toggleKeyInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Window Size:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultSizeInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Items per Page:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultPageLimitInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Enable Bitwise Editing:", new GUILayoutOption[] { GUILayout.Width(215f) });
bitwiseSupportInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Enable Tab View:", new GUILayoutOption[] { GUILayout.Width(215f) });
tabViewInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);
GUILayout.EndHorizontal();
UIStyles.HorizontalLine(Color.black, true);
GUIUnstrip.BeginHorizontal(new GUILayoutOption[0]);
GUILayout.Label($"Default Output Path:", new GUILayoutOption[] { GUILayout.Width(215f) });
defaultOutputPathInput.IValue.DrawValue(MainMenu.MainRect, MainMenu.MainRect.width - 215f);

View File

@ -8,18 +8,17 @@ namespace Explorer.UI.Shared
{
public class ResizeDrag
{
#if CPP
private static bool RESIZE_FAILED = false;
#endif
public static bool IsResizing = false;
public static bool IsMouseInResizeArea = false;
private static readonly GUIContent gcDrag = new GUIContent("<-- Drag to resize -->");
private static bool isResizing = false;
private static Rect m_currentResize;
private static int m_currentWindow;
public static Rect ResizeWindow(Rect _rect, int ID)
{
#if CPP
if (!RESIZE_FAILED)
{
var origRect = _rect;
@ -29,31 +28,40 @@ namespace Explorer.UI.Shared
GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
#if ML
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#if BIE
#if CPP // Temporary for BepInEx IL2CPP
GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) });
#else
GUILayout.Button("<-- Drag to resize -->", new GUILayoutOption[] { GUILayout.Height(15) });
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#endif
#else
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
#endif
var r = GUIUnstrip.GetLastRect();
var resizeDragArea = GUIUnstrip.GetLastRect();
var mousePos = InputManager.MousePosition;
try
{
var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputManager.GetMouseButtonDown(0))
if (resizeDragArea.Contains(mouse))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
IsMouseInResizeArea = true;
if (InputManager.GetMouseButton(0))
{
IsResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
}
else if (!InputManager.GetMouseButton(0))
{
isResizing = false;
IsMouseInResizeArea = false;
IsResizing = false;
}
if (isResizing && ID == m_currentWindow)
if (IsResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
@ -81,8 +89,6 @@ namespace Explorer.UI.Shared
//ExplorerCore.Log(e.StackTrace);
return origRect;
}
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
else
{
@ -111,44 +117,8 @@ namespace Explorer.UI.Shared
}
GUILayout.EndHorizontal();
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
#else // mono
GUIUnstrip.BeginHorizontal(GUIContent.none, GUI.skin.box, null);
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Button(gcDrag, GUI.skin.label, new GUILayoutOption[] { GUILayout.Height(15) });
//var r = GUILayoutUtility.GetLastRect();
var r = GUILayoutUtility.GetLastRect();
var mousePos = InputManager.MousePosition;
var mouse = GUIUnstrip.ScreenToGUIPoint(new Vector2(mousePos.x, Screen.height - mousePos.y));
if (r.Contains(mouse) && InputManager.GetMouseButtonDown(0))
{
isResizing = true;
m_currentWindow = ID;
m_currentResize = new Rect(mouse.x, mouse.y, _rect.width, _rect.height);
}
else if (!InputManager.GetMouseButton(0))
{
isResizing = false;
}
if (isResizing && ID == m_currentWindow)
{
_rect.width = Mathf.Max(100, m_currentResize.width + (mouse.x - m_currentResize.x));
_rect.height = Mathf.Max(100, m_currentResize.height + (mouse.y - m_currentResize.y));
_rect.xMax = Mathf.Min(Screen.width, _rect.xMax); // modifying xMax affects width, not x
_rect.yMax = Mathf.Min(Screen.height, _rect.yMax); // modifying yMax affects height, not y
}
GUILayout.EndHorizontal();
#endif
GUI.skin.label.alignment = TextAnchor.MiddleLeft;
return _rect;

View File

@ -70,6 +70,15 @@ namespace Explorer.UI
int rowCount = 0;
for (int i = 0; i < WindowManager.Windows.Count; i++)
{
var window = WindowManager.Windows[i];
// Prevent trying to draw destroyed UnityEngine.Objects
// before the WindowManager removes them.
if (window.Target is UnityEngine.Object uObj && !uObj)
{
continue;
}
if (rowCount >= tabPerRow)
{
rowCount = 0;
@ -82,7 +91,6 @@ namespace Explorer.UI
string color = focused ? "<color=lime>" : "<color=orange>";
GUI.color = focused ? Color.green : Color.white;
var window = WindowManager.Windows[i];
if (GUILayout.Button(color + window.Title + "</color>", new GUILayoutOption[] { GUILayout.Width(200) }))
{
TargetTabID = i;

View File

@ -11,6 +11,26 @@ namespace Explorer.UI
public static bool TabView = Config.ModConfig.Instance.Tab_View;
public static bool IsMouseInWindow
{
get
{
if (!ExplorerCore.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
public static List<WindowBase> Windows = new List<WindowBase>();
public static int CurrentWindowID { get; set; } = 500000;
private static Rect m_lastWindowRect;
@ -123,26 +143,6 @@ namespace Explorer.UI
return new_window;
}
public static bool IsMouseInWindow
{
get
{
if (!ExplorerCore.ShowMenu)
{
return false;
}
foreach (var window in Windows)
{
if (RectContainsMouse(window.m_rect))
{
return true;
}
}
return RectContainsMouse(MainMenu.MainRect);
}
}
private static bool RectContainsMouse(Rect rect)
{
var mousePos = InputManager.MousePosition;

View File

@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
#if CPP
using UnhollowerBaseLib;
#endif
namespace Explorer.Unstrip.LayerMasks
{
public static class LayerMaskUnstrip
{
#if CPP
internal delegate IntPtr d_LayerToName(int layer);
internal static d_LayerToName LayerToName_iCall =
IL2CPP.ResolveICall<d_LayerToName>("UnityEngine.LayerMask::LayerToName");
public static string LayerToName(int layer)
{
var ptr = LayerToName_iCall(layer);
return IL2CPP.Il2CppStringToManaged(ptr);
}
#else
public static string LayerToName(int layer)
{
return LayerMask.LayerToName(layer);
}
#endif
}
}

View File

@ -26,7 +26,7 @@ namespace Explorer.Unstrip.Scenes
IL2CPP.ResolveICall<GetRootGameObjectsInternal_delegate>("UnityEngine.SceneManagement.Scene::GetRootGameObjectsInternal");
//Scene.rootCount;
public static int GetRootCount_Internal(UnityEngine.SceneManagement.Scene scene)
public static int GetRootCount_Internal(Scene scene)
{
return GetRootCountInternal_iCall(scene.handle);
}