* Fixed a bug on the Reflection window which would prevent primitive values from being applied
* Improved some parts of the Scene Explorer and the Reflection Window interfaces
* Scene Explorer now has "page view" like other lists
* Various minor cleanups and refactorings
This commit is contained in:
sinaioutlander 2020-08-24 01:42:19 +10:00
parent e3d1add090
commit 45b5ce0ef8
15 changed files with 536 additions and 386 deletions

View File

@ -10,21 +10,18 @@ namespace Explorer
{
public class CacheEnum : CacheObject
{
private readonly Type m_enumType;
private readonly string[] m_names;
public Type EnumType;
public string[] EnumNames;
public CacheEnum(object obj)
public override void Init()
{
if (obj != null)
{
m_enumType = obj.GetType();
m_names = Enum.GetNames(m_enumType);
}
EnumType = Value.GetType();
EnumNames = Enum.GetNames(EnumType);
}
public override void DrawValue(Rect window, float width)
{
if (MemberInfo != null)
if (CanWrite)
{
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(25) }))
{
@ -38,34 +35,18 @@ namespace Explorer
}
}
GUILayout.Label(Value.ToString(), null);
}
public override void SetValue()
{
if (MemberInfo == null)
{
MelonLogger.Log("Trying to SetValue but the MemberInfo is null!");
return;
}
if (Enum.Parse(m_enumType, Value.ToString()) is object enumValue && enumValue != null)
{
Value = enumValue;
}
SetValue(Value, MemberInfo, DeclaringInstance);
GUILayout.Label(Value.ToString(), null);// + "<color=yellow><i> (" + ValueType + ")</i></color>", null);
}
public void SetEnum(ref object value, int change)
{
var names = m_names.ToList();
var names = EnumNames.ToList();
int newindex = names.IndexOf(value.ToString()) + change;
if ((change < 0 && newindex >= 0) || (change > 0 && newindex < names.Count))
{
value = Enum.Parse(m_enumType, names[newindex]);
value = Enum.Parse(EnumType, names[newindex]);
}
}
}

View File

@ -10,44 +10,37 @@ namespace Explorer
{
public class CacheGameObject : CacheObject
{
private GameObject m_gameObject;
public CacheGameObject(object obj)
private GameObject GameObj
{
if (obj != null)
m_gameObject = GetGameObject(obj);
}
private GameObject GetGameObject(object obj)
{
if (obj is Il2CppSystem.Object ilObj)
get
{
var ilType = ilObj.GetIl2CppType();
if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType)
if (m_gameObject == null)
{
return ilObj.TryCast<GameObject>() ?? ilObj.TryCast<Transform>()?.gameObject;
}
}
if (Value is Il2CppSystem.Object ilObj)
{
var ilType = ilObj.GetIl2CppType();
return null;
if (ilType == ReflectionHelpers.GameObjectType || ilType == ReflectionHelpers.TransformType)
{
m_gameObject = ilObj.TryCast<GameObject>() ?? ilObj.TryCast<Transform>()?.gameObject;
}
}
}
return m_gameObject;
}
}
private GameObject m_gameObject;
public override void DrawValue(Rect window, float width)
{
UIHelpers.GameobjButton(m_gameObject, null, false, width);
}
public override void SetValue()
{
throw new NotImplementedException("TODO");
UIHelpers.GameobjButton(GameObj, null, false, width);
}
public override void UpdateValue()
{
base.UpdateValue();
m_gameObject = GetGameObject(Value);
}
}
}

View File

@ -14,6 +14,7 @@ namespace Explorer
{
public bool IsExpanded { get; set; }
public int ArrayOffset { get; set; }
public int ArrayLimit { get; set; } = 20;
public Type EntryType
{
@ -47,15 +48,6 @@ namespace Explorer
private IEnumerable m_enumerable;
private CacheObject[] m_cachedEntries;
public CacheList(object obj)
{
if (obj != null)
{
Value = obj;
EntryType = obj.GetType().GetGenericArguments()[0];
}
}
private IEnumerable CppListToEnumerable(object list)
{
if (EntryType == null) return null;
@ -95,12 +87,12 @@ namespace Explorer
if (IsExpanded)
{
if (count > CppExplorer.ArrayLimit)
if (count > ArrayLimit)
{
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
GUILayout.Space(190);
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)CppExplorer.ArrayLimit)) - 1;
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)ArrayLimit)) - 1;
GUILayout.Label($"Page {ArrayOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
// prev/next page buttons
if (GUILayout.Button("< Prev", null))
@ -111,13 +103,20 @@ namespace Explorer
{
if (ArrayOffset < maxOffset) ArrayOffset++;
}
GUILayout.Label("Limit: ", new GUILayoutOption[] { GUILayout.Width(50) });
var limit = this.ArrayLimit.ToString();
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(50) });
if (limit != ArrayLimit.ToString() && int.TryParse(limit, out int i))
{
ArrayLimit = i;
}
}
int offset = ArrayOffset * CppExplorer.ArrayLimit;
int offset = ArrayOffset * ArrayLimit;
if (offset >= count) offset = 0;
for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++)
for (int i = offset; i < offset + ArrayLimit && i < count; i++)
{
var entry = m_cachedEntries[i];
@ -140,11 +139,6 @@ namespace Explorer
}
}
public override void SetValue()
{
throw new NotImplementedException("TODO");
}
/// <summary>
/// Called when the user presses the "Update" button, or if AutoUpdate is on.
/// </summary>
@ -161,7 +155,7 @@ namespace Explorer
var list = new List<CacheObject>();
while (enumerator.MoveNext())
{
list.Add(GetCacheObject(enumerator.Current));
list.Add(GetCacheObject(enumerator.Current, null, null, this.EntryType));
}
m_cachedEntries = list.ToArray();

View File

@ -17,15 +17,45 @@ namespace Explorer
// Reflection window only
public MemberInfo MemberInfo { get; set; }
public ReflectionWindow.MemberInfoType MemberInfoType { get; set; }
// public ReflectionWindow.MemberInfoType MemberInfoType { get; set; }
public Type DeclaringType { get; set; }
public object DeclaringInstance { get; set; }
public string FullName => $"{MemberInfo.DeclaringType.Name}.{MemberInfo.Name}";
public string ReflectionException;
public bool CanWrite
{
get
{
if (MemberInfo is FieldInfo fi)
{
return !(fi.IsLiteral && !fi.IsInitOnly);
}
else if (MemberInfo is PropertyInfo pi)
{
return pi.CanWrite;
}
else
{
return false;
}
}
}
public ReflectionWindow.MemberInfoType MemberInfoType
{
get
{
if (MemberInfo is FieldInfo) return ReflectionWindow.MemberInfoType.Field;
if (MemberInfo is PropertyInfo) return ReflectionWindow.MemberInfoType.Property;
if (MemberInfo is MethodInfo) return ReflectionWindow.MemberInfoType.Method;
return ReflectionWindow.MemberInfoType.All;
}
}
// methods
public virtual void Init() { }
public abstract void DrawValue(Rect window, float width);
public abstract void SetValue();
public static CacheObject GetCacheObject(object obj)
{
@ -41,63 +71,72 @@ namespace Explorer
/// <returns></returns>
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance)
{
CacheObject holder;
var type = ReflectionHelpers.GetActualType(obj) ?? (memberInfo as FieldInfo)?.FieldType ?? (memberInfo as PropertyInfo)?.PropertyType;
if (type == null)
{
MelonLogger.Log("Could not get type for object or memberinfo!");
return null;
}
return GetCacheObject(obj, memberInfo, declaringInstance, type);
}
/// <summary>
/// Gets the CacheObject subclass for an object or MemberInfo
/// </summary>
/// <param name="obj">The current value (can be null if memberInfo is not null)</param>
/// <param name="memberInfo">The MemberInfo (can be null if obj is not null)</param>
/// <param name="declaringInstance">If MemberInfo is not null, the declaring class instance. Can be null if static.</param>
/// <param name="type">The type of the object or MemberInfo value.</param>
/// <returns></returns>
public static CacheObject GetCacheObject(object obj, MemberInfo memberInfo, object declaringInstance, Type type)
{
CacheObject holder;
if ((obj is Il2CppSystem.Object || typeof(Il2CppSystem.Object).IsAssignableFrom(type))
&& (type.FullName.Contains("UnityEngine.GameObject") || type.FullName.Contains("UnityEngine.Transform")))
{
holder = new CacheGameObject(obj);
holder = new CacheGameObject();
}
else if (type.IsPrimitive || type == typeof(string))
{
holder = new CachePrimitive(obj);
holder = new CachePrimitive();
}
else if (type.IsEnum)
{
holder = new CacheEnum(obj);
holder = new CacheEnum();
}
else if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type) || ReflectionHelpers.IsList(type))
{
holder = new CacheList(obj);
holder = new CacheList();
}
else
{
holder = new CacheOther();
}
if (memberInfo != null)
{
holder.MemberInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
if (memberInfo.MemberType == MemberTypes.Field)
{
holder.MemberInfoType = ReflectionWindow.MemberInfoType.Field;
}
else if (memberInfo.MemberType == MemberTypes.Property)
{
holder.MemberInfoType = ReflectionWindow.MemberInfoType.Property;
}
else if (memberInfo.MemberType == MemberTypes.Method)
{
holder.MemberInfoType = ReflectionWindow.MemberInfoType.Method;
}
}
holder.Value = obj;
holder.ValueType = type.FullName;
if (memberInfo != null)
{
holder.MemberInfo = memberInfo;
holder.DeclaringType = memberInfo.DeclaringType;
holder.DeclaringInstance = declaringInstance;
}
holder.UpdateValue();
holder.Init();
return holder;
}
public void Draw(Rect window, float labelWidth = 180f)
public void Draw(Rect window, float labelWidth = 215f)
{
if (MemberInfo != null)
{
GUILayout.Label("<color=cyan>" + FullName + ":</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
GUILayout.Label("<color=cyan>" + FullName + "</color>", new GUILayoutOption[] { GUILayout.Width(labelWidth) });
}
else
{
@ -110,7 +149,7 @@ namespace Explorer
}
else if (Value == null)
{
GUILayout.Label("<i>null (" + this.ValueType + ")</i>", null);
GUILayout.Label("<i>null (" + ValueType + ")</i>", null);
}
else
{
@ -147,25 +186,19 @@ namespace Explorer
}
}
public void SetValue(object value, MemberInfo memberInfo, object declaringInstance)
public void SetValue()
{
try
{
if (memberInfo.MemberType == MemberTypes.Field)
if (MemberInfo.MemberType == MemberTypes.Field)
{
var fi = memberInfo as FieldInfo;
if (!(fi.IsLiteral && !fi.IsInitOnly))
{
fi.SetValue(fi.IsStatic ? null : declaringInstance, value);
}
var fi = MemberInfo as FieldInfo;
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, Value);
}
else if (memberInfo.MemberType == MemberTypes.Property)
else if (MemberInfo.MemberType == MemberTypes.Property)
{
var pi = memberInfo as PropertyInfo;
if (pi.CanWrite)
{
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : declaringInstance, value);
}
var pi = MemberInfo as PropertyInfo;
pi.SetValue(pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance, Value);
}
}
catch (Exception e)

View File

@ -58,21 +58,11 @@ namespace Explorer
}
GUI.skin.button.alignment = TextAnchor.MiddleLeft;
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width) }))
if (GUILayout.Button("<color=yellow>" + label + "</color>", new GUILayoutOption[] { GUILayout.MaxWidth(width + 40) }))
{
WindowManager.InspectObject(Value, out bool _);
}
GUI.skin.button.alignment = TextAnchor.MiddleCenter;
}
public override void SetValue()
{
throw new NotImplementedException("TODO");
}
//public override void UpdateValue(object obj)
//{
//}
}
}

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using MelonLoader;
using UnityEngine;
@ -10,7 +7,7 @@ namespace Explorer
{
public class CachePrimitive : CacheObject
{
public enum PrimitiveType
public enum PrimitiveTypes
{
Bool,
Double,
@ -19,69 +16,122 @@ namespace Explorer
String
}
private readonly PrimitiveType m_primitiveType;
private string m_valueToString;
public CachePrimitive(object obj)
public PrimitiveTypes PrimitiveType;
public MethodInfo ParseMethod
{
if (obj == null) return;
get
{
if (m_parseMethod == null)
{
Type t = null;
switch (PrimitiveType)
{
case PrimitiveTypes.Bool:
t = typeof(bool); break;
case PrimitiveTypes.Double:
t = typeof(double); break;
case PrimitiveTypes.Float:
t = typeof(float); break;
case PrimitiveTypes.Int:
t = typeof(int); break;
case PrimitiveTypes.String:
t = typeof(string); break;
}
m_parseMethod = t.GetMethod("Parse", new Type[] { typeof(string) });
}
return m_parseMethod;
}
}
if (obj is bool)
private MethodInfo m_parseMethod;
public override void Init()
{
if (Value == null)
{
m_primitiveType = PrimitiveType.Bool;
// this must mean it is a string? no other primitive type should be nullable
PrimitiveType = PrimitiveTypes.String;
return;
}
else if (obj is double)
m_valueToString = Value.ToString();
var type = Value.GetType();
if (type == typeof(bool))
{
m_primitiveType = PrimitiveType.Double;
PrimitiveType = PrimitiveTypes.Bool;
}
else if (obj is float)
else if (type == typeof(double))
{
m_primitiveType = PrimitiveType.Float;
PrimitiveType = PrimitiveTypes.Double;
}
else if (obj is int || obj is IntPtr || obj is uint)
else if (type == typeof(float))
{
m_primitiveType = PrimitiveType.Int;
PrimitiveType = PrimitiveTypes.Float;
}
else if (obj is string)
else if (type == typeof(int) || type == typeof(long) || type == typeof(uint) || type == typeof(ulong) || type == typeof(IntPtr))
{
m_primitiveType = PrimitiveType.String;
PrimitiveType = PrimitiveTypes.Int;
}
else
{
if (type != typeof(string))
{
MelonLogger.Log("Unsupported primitive: " + type);
}
PrimitiveType = PrimitiveTypes.String;
}
}
public override void UpdateValue()
{
base.UpdateValue();
m_valueToString = Value?.ToString();
}
public override void DrawValue(Rect window, float width)
{
if (m_primitiveType == PrimitiveType.Bool && Value is bool b)
if (PrimitiveType == PrimitiveTypes.Bool)
{
var b = (bool)Value;
var color = "<color=" + (b ? "lime>" : "red>");
Value = GUILayout.Toggle((bool)Value, color + Value.ToString() + "</color>", null);
b = GUILayout.Toggle(b, color + b.ToString() + "</color>", null);
if (b != (bool)Value)
{
SetValue();
SetValue(m_valueToString);
}
}
else
{
var toString = Value.ToString();
if (toString.Length > 37)
GUILayout.Label("<color=yellow><i>" + PrimitiveType + "</i></color>", new GUILayoutOption[] { GUILayout.Width(50) });
var _width = window.width - 200;
if (m_valueToString.Length > 37)
{
Value = GUILayout.TextArea(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) });
m_valueToString = GUILayout.TextArea(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) });
}
else
{
Value = GUILayout.TextField(toString, new GUILayoutOption[] { GUILayout.MaxWidth(window.width - 260) });
m_valueToString = GUILayout.TextField(m_valueToString, new GUILayoutOption[] { GUILayout.MaxWidth(_width) });
}
if (MemberInfo != null)
if (CanWrite)
{
if (GUILayout.Button("<color=#00FF00>Apply</color>", new GUILayoutOption[] { GUILayout.Width(60) }))
{
SetValue();
SetValue(m_valueToString);
}
}
}
}
public override void SetValue()
public void SetValue(string value)
{
if (MemberInfo == null)
{
@ -89,19 +139,24 @@ namespace Explorer
return;
}
switch (m_primitiveType)
if (PrimitiveType == PrimitiveTypes.String)
{
case PrimitiveType.Bool:
SetValue(bool.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return;
case PrimitiveType.Double:
SetValue(double.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return;
case PrimitiveType.Float:
SetValue(float.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return;
case PrimitiveType.Int:
SetValue(int.Parse(Value.ToString()), MemberInfo, DeclaringInstance); return;
case PrimitiveType.String:
SetValue(Value.ToString(), MemberInfo, DeclaringInstance); return;
Value = value;
}
else
{
try
{
var val = ParseMethod.Invoke(null, new object[] { value });
Value = val;
}
catch (Exception e)
{
MelonLogger.Log("Exception parsing value: " + e.GetType() + ", " + e.Message);
}
}
SetValue();
}
}
}

View File

@ -13,7 +13,7 @@ namespace Explorer
// consts
public const string ID = "com.sinai.cppexplorer";
public const string VERSION = "1.4.1";
public const string VERSION = "1.4.2";
public const string AUTHOR = "Sinai";
public const string NAME = "CppExplorer"
@ -29,7 +29,6 @@ namespace Explorer
// props
public static bool ShowMenu { get; set; } = false;
public static int ArrayLimit { get; set; } = 20;
// methods

View File

@ -22,6 +22,7 @@ namespace Explorer
public static Il2CppSystem.Type TransformType => Il2CppType.Of<Transform>();
public static Il2CppSystem.Type ObjectType => Il2CppType.Of<UnityEngine.Object>();
public static Il2CppSystem.Type ComponentType => Il2CppType.Of<Component>();
public static Il2CppSystem.Type BehaviourType => Il2CppType.Of<Behaviour>();
private static readonly MethodInfo m_tryCastMethodInfo = typeof(Il2CppObjectBase).GetMethod("TryCast");

View File

@ -22,7 +22,7 @@ namespace Explorer
}
// helper for drawing a styled button for a GameObject or Transform
public static void GameobjButton(GameObject obj, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
public static void GameobjButton(GameObject obj, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
bool children = obj.transform.childCount > 0;
@ -52,7 +52,7 @@ namespace Explorer
FastGameobjButton(obj, color, label, obj.activeSelf, specialInspectMethod, showSmallInspectBtn, width);
}
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<GameObject> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
public static void FastGameobjButton(GameObject obj, Color activeColor, string label, bool enabled, Action<Transform> specialInspectMethod = null, bool showSmallInspectBtn = true, float width = 380)
{
if (!obj)
{
@ -79,7 +79,7 @@ namespace Explorer
{
if (specialInspectMethod != null)
{
specialInspectMethod(obj);
specialInspectMethod(obj.transform);
}
else
{
@ -94,13 +94,18 @@ namespace Explorer
if (showSmallInspectBtn)
{
if (GUILayout.Button("Inspect", null))
{
WindowManager.InspectObject(obj, out bool _);
}
SmallInspectButton(obj);
}
GUILayout.EndHorizontal();
}
public static void SmallInspectButton(object obj)
{
if (GUILayout.Button("Inspect", null))
{
WindowManager.InspectObject(obj, out bool _);
}
}
}
}

View File

@ -88,19 +88,6 @@ namespace Explorer
private void MainHeader()
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Options:</b>", new GUILayoutOption[] { GUILayout.Width(70) });
GUI.skin.label.alignment = TextAnchor.MiddleRight;
GUILayout.Label("Array Limit:", new GUILayoutOption[] { GUILayout.Width(70) });
GUI.skin.label.alignment = TextAnchor.UpperLeft;
var _input = GUILayout.TextField(CppExplorer.ArrayLimit.ToString(), new GUILayoutOption[] { GUILayout.Width(60) });
if (int.TryParse(_input, out int _lim))
{
CppExplorer.ArrayLimit = _lim;
}
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
GUILayout.EndHorizontal();
GUILayout.BeginHorizontal(null);
for (int i = 0; i < Pages.Count; i++)
{
@ -116,6 +103,9 @@ namespace Explorer
}
GUILayout.EndHorizontal();
GUI.color = Color.white;
InspectUnderMouse.EnableInspect = GUILayout.Toggle(InspectUnderMouse.EnableInspect, "Inspect Under Mouse (Shift + RMB)", null);
GUILayout.Space(10);
GUI.color = Color.white;
}

View File

@ -14,9 +14,15 @@ namespace Explorer
public override string Name { get => "Scene Explorer"; set => base.Name = value; }
// ----- Holders for GUI elements ----- //
private int m_pageOffset = 0;
private int m_limit = 20;
private int m_currentTotalCount = 0;
private string m_currentScene = "";
private float m_timeOfLastUpdate = -1f;
// ----- Holders for GUI elements ----- //
private string m_currentScene = "";
// gameobject list
private Transform m_currentTransform;
@ -37,190 +43,84 @@ namespace Explorer
public void OnSceneChange()
{
m_currentScene = UnityHelpers.ActiveSceneName;
SetTransformTarget(null);
}
m_currentTransform = null;
CancelSearch();
public void CheckOffset(ref int offset, int childCount)
{
if (offset >= childCount)
{
offset = 0;
m_pageOffset = 0;
}
}
public override void Update()
{
if (!m_searching)
if (m_searching) return;
if (Time.time - m_timeOfLastUpdate < 1f) return;
m_timeOfLastUpdate = Time.time;
m_objectList = new List<GameObjectCache>();
int offset = m_pageOffset * m_limit;
var allTransforms = new List<Transform>();
// get current list of all transforms (either scene root or our current transform children)
if (m_currentTransform)
{
m_objectList = new List<GameObjectCache>();
if (m_currentTransform)
for (int i = 0; i < m_currentTransform.childCount; i++)
{
var endAppend = new List<GameObjectCache>();
for (int i = 0; i < m_currentTransform.childCount; i++)
{
var child = m_currentTransform.GetChild(i);
if (child)
{
if (child.childCount > 0)
m_objectList.Add(new GameObjectCache(child.gameObject));
else
endAppend.Add(new GameObjectCache(child.gameObject));
}
}
m_objectList.AddRange(endAppend);
endAppend = null;
allTransforms.Add(m_currentTransform.GetChild(i));
}
else
}
else
{
var scene = SceneManager.GetSceneByName(m_currentScene);
var rootObjects = scene.GetRootGameObjects();
foreach (var obj in rootObjects)
{
var scene = SceneManager.GetSceneByName(m_currentScene);
var rootObjects = scene.GetRootGameObjects();
// add objects with children first
foreach (var obj in rootObjects.Where(x => x.transform.childCount > 0))
{
m_objectList.Add(new GameObjectCache(obj));
}
foreach (var obj in rootObjects.Where(x => x.transform.childCount == 0))
{
m_objectList.Add(new GameObjectCache(obj));
}
allTransforms.Add(obj.transform);
}
}
m_currentTotalCount = allTransforms.Count;
// make sure offset doesn't exceed count
CheckOffset(ref offset, m_currentTotalCount);
// sort by childcount
allTransforms.Sort((a, b) => b.childCount.CompareTo(a.childCount));
for (int i = offset; i < offset + m_limit && i < m_currentTotalCount; i++)
{
var child = allTransforms[i];
m_objectList.Add(new GameObjectCache(child.gameObject));
}
}
// --------- GUI Draw Functions --------- //
public override void DrawWindow()
public void SetTransformTarget(Transform t)
{
try
{
GUILayout.BeginHorizontal(null);
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
try
{
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
var scenes = SceneManager.GetAllScenes().ToList();
m_currentTransform = t;
if (scenes.Count > 1)
{
int changeWanted = 0;
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
{
changeWanted = -1;
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
changeWanted = 1;
}
if (changeWanted != 0)
{
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
index += changeWanted;
if (index > scenes.Count - 1)
{
index = 0;
}
else if (index < 0)
{
index = scenes.Count - 1;
}
m_currentScene = scenes[index].name;
}
}
}
catch { }
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
if (m_searching)
CancelSearch();
GUILayout.EndHorizontal();
// ----- GameObject Search -----
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, null);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Search();
}
GUILayout.EndHorizontal();
GUILayout.Space(15);
// ************** GameObject list ***************
// ----- main explorer ------
if (!m_searching)
{
if (m_currentTransform != null)
{
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
TraverseUp();
}
else
{
GUILayout.Label(m_currentTransform.GetGameObjectPath(), null);
}
GUILayout.EndHorizontal();
}
else
{
GUILayout.Label("Scene Root GameObjects:", null);
}
if (m_objectList.Count > 0)
{
foreach (var obj in m_objectList)
{
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
}
}
else // ------ Scene Search results ------
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
CancelSearch();
}
GUILayout.Label("Search Results:", null);
if (m_searchResults.Count > 0)
{
foreach (var obj in m_searchResults)
{
//UIStyles.GameobjButton(obj, SetTransformTarget, true, MainMenu.MainRect.width - 170);
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
}
}
}
catch
{
m_currentTransform = null;
}
}
// -------- Actual Methods (not drawing GUI) ---------- //
public void SetTransformTarget(GameObject obj)
{
m_currentTransform = obj.transform;
CancelSearch();
m_timeOfLastUpdate = -1f;
Update();
}
public void TraverseUp()
{
if (m_currentTransform.parent != null)
{
m_currentTransform = m_currentTransform.parent;
SetTransformTarget(m_currentTransform.parent);
}
else
{
m_currentTransform = null;
SetTransformTarget(null);
}
}
@ -228,6 +128,7 @@ namespace Explorer
{
m_searchResults = SearchSceneObjects(m_searchInput);
m_searching = true;
m_currentTotalCount = m_searchResults.Count;
}
public void CancelSearch()
@ -249,6 +150,220 @@ namespace Explorer
return matches;
}
// --------- GUI Draw Function --------- //
public override void DrawWindow()
{
try
{
DrawHeaderArea();
GUILayout.BeginVertical(GUI.skin.box, null);
DrawPageButtons();
if (!m_searching)
{
DrawGameObjectList();
}
else
{
DrawSearchResultsList();
}
GUILayout.EndVertical();
}
catch (Exception e)
{
MelonLogger.Log("Exception drawing ScenePage! " + e.GetType() + ", " + e.Message);
MelonLogger.Log(e.StackTrace);
m_currentTransform = null;
}
}
private void DrawHeaderArea()
{
GUILayout.BeginHorizontal(null);
// Current Scene label
GUILayout.Label("Current Scene:", new GUILayoutOption[] { GUILayout.Width(120) });
try
{
// Need to do 'ToList()' so the object isn't cleaned up by Il2Cpp GC.
var scenes = SceneManager.GetAllScenes().ToList();
if (scenes.Count > 1)
{
int changeWanted = 0;
if (GUILayout.Button("<", new GUILayoutOption[] { GUILayout.Width(30) }))
{
changeWanted = -1;
}
if (GUILayout.Button(">", new GUILayoutOption[] { GUILayout.Width(30) }))
{
changeWanted = 1;
}
if (changeWanted != 0)
{
int index = scenes.IndexOf(SceneManager.GetSceneByName(m_currentScene));
index += changeWanted;
if (index > scenes.Count - 1)
{
index = 0;
}
else if (index < 0)
{
index = scenes.Count - 1;
}
m_currentScene = scenes[index].name;
}
}
}
catch { }
GUILayout.Label("<color=cyan>" + m_currentScene + "</color>", null); //new GUILayoutOption[] { GUILayout.Width(250) });
GUILayout.EndHorizontal();
// ----- GameObject Search -----
GUILayout.BeginHorizontal(GUI.skin.box, null);
GUILayout.Label("<b>Search Scene:</b>", new GUILayoutOption[] { GUILayout.Width(100) });
m_searchInput = GUILayout.TextField(m_searchInput, null);
if (GUILayout.Button("Search", new GUILayoutOption[] { GUILayout.Width(80) }))
{
Search();
}
GUILayout.EndHorizontal();
GUILayout.Space(5);
}
private void DrawPageButtons()
{
GUILayout.BeginHorizontal(null);
GUILayout.Label("Limit per page: ", new GUILayoutOption[] { GUILayout.Width(100) });
var limit = m_limit.ToString();
limit = GUILayout.TextField(limit, new GUILayoutOption[] { GUILayout.Width(30) });
if (int.TryParse(limit, out int lim))
{
m_limit = lim;
}
// prev/next page buttons
if (m_currentTotalCount > m_limit)
{
int count = m_currentTotalCount;
int maxOffset = (int)Mathf.Ceil((float)(count / (decimal)m_limit)) - 1;
if (GUILayout.Button("< Prev", null))
{
if (m_pageOffset > 0) m_pageOffset--;
m_timeOfLastUpdate = -1f;
Update();
}
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
if (GUILayout.Button("Next >", null))
{
if (m_pageOffset < maxOffset) m_pageOffset++;
m_timeOfLastUpdate = -1f;
Update();
}
}
GUILayout.EndHorizontal();
GUI.skin.label.alignment = TextAnchor.UpperLeft;
}
private void DrawGameObjectList()
{
if (m_currentTransform != null)
{
GUILayout.BeginHorizontal(null);
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
TraverseUp();
}
else
{
GUILayout.Label("<color=cyan>" + m_currentTransform.GetGameObjectPath() + "</color>",
new GUILayoutOption[] { GUILayout.Width(MainMenu.MainRect.width - 187f) });
}
UIHelpers.SmallInspectButton(m_currentTransform);
GUILayout.EndHorizontal();
}
else
{
GUILayout.Label("Scene Root GameObjects:", null);
}
if (m_objectList.Count > 0)
{
foreach (var obj in m_objectList)
{
if (!obj.RefGameObject)
{
string label = "<color=red><i>null";
if (obj.RefGameObject != null)
{
label += " (Destroyed)";
}
label += "</i></color>";
GUILayout.Label(label, null);
}
else
{
UIHelpers.FastGameobjButton(obj.RefGameObject,
obj.EnabledColor,
obj.Label,
obj.RefGameObject.activeSelf,
SetTransformTarget,
true,
MainMenu.MainRect.width - 170);
}
}
}
}
private void DrawSearchResultsList()
{
if (GUILayout.Button("<- Cancel Search", new GUILayoutOption[] { GUILayout.Width(150) }))
{
CancelSearch();
}
GUILayout.Label("Search Results:", null);
if (m_searchResults.Count > 0)
{
int offset = m_pageOffset * m_limit;
if (offset >= m_searchResults.Count)
{
offset = 0;
m_pageOffset = 0;
}
for (int i = offset; i < offset + m_limit && offset < m_searchResults.Count; i++)
{
var obj = m_searchResults[i];
UIHelpers.FastGameobjButton(obj.RefGameObject, obj.EnabledColor, obj.Label, obj.RefGameObject.activeSelf, SetTransformTarget, true, MainMenu.MainRect.width - 170);
}
}
else
{
GUILayout.Label("<color=red><i>No results found!</i></color>", null);
}
}
// -------- Mini GameObjectCache class ---------- //
public class GameObjectCache
{

View File

@ -135,7 +135,7 @@ namespace Explorer
int offset = m_pageOffset * this.m_limit;
if (offset >= count) m_pageOffset = 0;
for (int i = offset; i < offset + CppExplorer.ArrayLimit && i < count; i++)
for (int i = offset; i < offset + m_limit && i < count; i++)
{
m_searchResults[i].Draw(MainMenu.MainRect, 0f);
//m_searchResults[i].DrawValue(MainMenu.MainRect);

View File

@ -29,7 +29,7 @@ namespace Explorer
private float m_rotateAmount = 50f;
private float m_scaleAmount = 0.1f;
List<Component> m_cachedDestroyList = new List<Component>();
private List<Component> m_cachedDestroyList = new List<Component>();
//private string m_addComponentInput = "";
private string m_setParentInput = "";
@ -91,7 +91,7 @@ namespace Explorer
}
}
private void InspectGameObject(GameObject obj)
private void InspectGameObject(Transform obj)
{
var window = WindowManager.InspectObject(obj, out bool created);
@ -137,7 +137,7 @@ namespace Explorer
{
if (GUILayout.Button("<color=#00FF00>< View in Scene Explorer</color>", new GUILayoutOption[] { GUILayout.Width(230) }))
{
ScenePage.Instance.SetTransformTarget(m_object);
ScenePage.Instance.SetTransformTarget(m_object.transform);
MainMenu.SetCurrentPage(0);
}
}
@ -150,7 +150,7 @@ namespace Explorer
{
if (GUILayout.Button("<-", new GUILayoutOption[] { GUILayout.Width(35) }))
{
InspectGameObject(m_object.transform.parent.gameObject);
InspectGameObject(m_object.transform.parent);
}
}
GUILayout.TextArea(pathlabel, null);
@ -235,18 +235,16 @@ namespace Explorer
var m_components = new Il2CppSystem.Collections.Generic.List<Component>();
m_object.GetComponentsInternal(Il2CppType.Of<Component>(), false, false, true, false, m_components);
var ilTypeOfTransform = Il2CppType.Of<Transform>();
var ilTypeOfBehaviour = Il2CppType.Of<Behaviour>();
foreach (var component in m_components)
{
var ilType = component.GetIl2CppType();
if (ilType == ilTypeOfTransform)
if (ilType == ReflectionHelpers.TransformType)
{
continue;
}
GUILayout.BeginHorizontal(null);
if (ilTypeOfBehaviour.IsAssignableFrom(ilType))
if (ReflectionHelpers.BehaviourType.IsAssignableFrom(ilType))
{
BehaviourEnabledBtn(component.TryCast<Behaviour>());
}
@ -274,24 +272,6 @@ namespace Explorer
GUILayout.EndScrollView();
//GUILayout.BeginHorizontal(null);
//m_addComponentInput = GUILayout.TextField(m_addComponentInput, new GUILayoutOption[] { GUILayout.Width(m_rect.width / 2 - 150) });
//if (GUILayout.Button("Add Component", new GUILayoutOption[] { GUILayout.Width(120) }))
//{
// if (HPExplorer.GetType(m_addComponentInput) is Type type && typeof(Component).IsAssignableFrom(type))
// {
// var comp = m_object.AddComponent(type);
// var list = m_components.ToList();
// list.Add(comp);
// m_components = list.ToArray();
// }
// else
// {
// MelonLogger.LogWarning($"Could not get type '{m_addComponentInput}'. If it's not a typo, try the fully qualified name.");
// }
//}
//GUILayout.EndHorizontal();
GUILayout.EndVertical();
}

View File

@ -25,7 +25,7 @@ namespace Explorer
private bool m_autoUpdate = false;
private string m_search = "";
public MemberInfoType m_filter = MemberInfoType.Property;
private bool m_hideFailedReflection = true;
private bool m_hideFailedReflection = false;
public enum MemberInfoType
{
@ -135,13 +135,25 @@ namespace Explorer
{
if (member.Name == "Il2CppType") continue;
var name = member.DeclaringType.Name + "." + member.Name;
if (names.Contains(name)) continue;
names.Add(name);
try
{
var name = member.DeclaringType.Name + "." + member.Name;
if (names.Contains(name)) continue;
names.Add(name);
var cached = CacheObject.GetCacheObject(null, member, target);
list.Add(cached);
cached.ReflectionException = exception;
var cached = CacheObject.GetCacheObject(null, member, target);
if (cached != null)
{
list.Add(cached);
cached.ReflectionException = exception;
}
}
catch (Exception e)
{
MelonLogger.Log("Exception caching member!");
MelonLogger.Log(e.GetType() + ", " + e.Message);
MelonLogger.Log(e.StackTrace);
}
}
}
}
@ -160,7 +172,7 @@ namespace Explorer
GUILayout.BeginArea(new Rect(5, 25, m_rect.width - 10, m_rect.height - 35), GUI.skin.box);
GUILayout.BeginHorizontal(null);
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.Name + "</color>", null);
GUILayout.Label("<b>Type:</b> <color=cyan>" + ObjectType.FullName + "</color>", null);
bool unityObj = Target is UnityEngine.Object;
@ -238,6 +250,7 @@ namespace Explorer
if (GUILayout.Button("< Prev", null))
{
if (m_pageOffset > 0) m_pageOffset--;
scroll = Vector2.zero;
}
GUILayout.Label($"Page {m_pageOffset + 1}/{maxOffset + 1}", new GUILayoutOption[] { GUILayout.Width(80) });
@ -245,6 +258,7 @@ namespace Explorer
if (GUILayout.Button("Next >", null))
{
if (m_pageOffset < maxOffset) m_pageOffset++;
scroll = Vector2.zero;
}
GUILayout.EndHorizontal();
}

View File

@ -104,7 +104,7 @@ namespace Explorer
foreach (var window in Windows)
{
if (obj == window.Target)
if (ReferenceEquals(obj, window.Target))
{
GUI.BringWindowToFront(window.windowID);
GUI.FocusWindow(window.windowID);