Compare commits

...

14 Commits
4.6.0 ... 4.6.3

Author SHA1 Message Date
7a5570a070 Bump version 2022-04-09 18:59:34 +10:00
d40537775f Bump UniverseLib 2022-04-09 18:59:13 +10:00
9b6f3fd3ea Cleanups / refactoring 2022-04-09 18:58:56 +10:00
bacac929e9 Show InputField for exceptions to view/copy full exception 2022-04-01 18:14:50 +11:00
4e3d3a2e5c Update README.md 2022-04-01 01:37:51 +11:00
7dbc8fd66e Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-04-01 01:31:53 +11:00
892cefcc91 Bump version 2022-04-01 01:31:45 +11:00
a986b92963 Don't try to get UnityObjectWidget for static class inspection 2022-04-01 01:31:40 +11:00
8837119781 Merge pull request #132 from liesauer/master
Fix standalone reading config item type casting bug
2022-04-01 01:21:39 +11:00
7eda249ddb Fix standalone reading config item type casting bug 2022-03-31 21:07:06 +08:00
710b4ba74a Use switch expression instead of if/else block 2022-03-29 22:39:26 +11:00
4bee55fb25 Cleanups, remove redundancy 2022-03-29 22:36:17 +11:00
c71748d22a Remove pointless ToArray() call 2022-03-23 19:02:17 +11:00
621035c732 Bump UniverseLib 2022-03-23 18:04:27 +11:00
22 changed files with 165 additions and 188 deletions

View File

@ -45,7 +45,7 @@
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually. The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually.
1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod 1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod. Take them from the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release if you need them.
2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup) 2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup)
2. Load the UnityExplorer DLL 2. Load the UnityExplorer DLL
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();` 3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`

View File

@ -72,13 +72,11 @@ namespace UnityExplorer.CacheObject
else else
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs); ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null; LastException = null;
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)
{ {
HadException = true;
LastException = ex; LastException = ex;
return null; return null;
} }

View File

@ -32,13 +32,11 @@ namespace UnityExplorer.CacheObject
try try
{ {
var ret = FieldInfo.GetValue(DeclaringInstance); var ret = FieldInfo.GetValue(DeclaringInstance);
HadException = false;
LastException = null; LastException = null;
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)
{ {
HadException = true;
LastException = ex; LastException = ex;
return null; return null;
} }

View File

@ -14,7 +14,7 @@ namespace UnityExplorer.CacheObject
{ {
public static class CacheMemberFactory public static class CacheMemberFactory
{ {
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector) public static List<CacheMember> GetCacheMembers(Type type, ReflectionInspector inspector)
{ {
//var list = new List<CacheMember>(); //var list = new List<CacheMember>();
HashSet<string> cachedSigs = new(); HashSet<string> cachedSigs = new();
@ -49,10 +49,6 @@ namespace UnityExplorer.CacheObject
foreach (var declaringType in types) foreach (var declaringType in types)
{ {
var target = inspectorTarget;
if (!inspector.StaticOnly)
target = target.TryCast(declaringType);
foreach (var prop in declaringType.GetProperties(flags)) foreach (var prop in declaringType.GetProperties(flags))
if (prop.DeclaringType == declaringType) if (prop.DeclaringType == declaringType)
TryCacheMember(prop, props, cachedSigs, declaringType, inspector); TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
@ -79,13 +75,9 @@ namespace UnityExplorer.CacheObject
return sorted; return sorted;
} }
static void TryCacheMember( static void TryCacheMember<T>(MemberInfo member, List<T> list, HashSet<string> cachedSigs,
MemberInfo member, Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
IList list, where T : CacheMember
HashSet<string> cachedSigs,
Type declaringType,
ReflectionInspector inspector,
bool ignorePropertyMethodInfos = true)
{ {
try try
{ {
@ -94,7 +86,9 @@ namespace UnityExplorer.CacheObject
string sig = member switch string sig = member switch
{ {
// method or constructor
MethodBase mb => mb.FullDescription(), MethodBase mb => mb.FullDescription(),
// property or field
PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}", PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}",
_ => throw new NotImplementedException(), _ => throw new NotImplementedException(),
}; };
@ -164,32 +158,13 @@ namespace UnityExplorer.CacheObject
cached.SetFallbackType(returnType); cached.SetFallbackType(returnType);
cached.SetInspectorOwner(inspector, member); cached.SetInspectorOwner(inspector, member);
list.Add(cached); list.Add((T)cached);
} }
catch (Exception e) catch (Exception e)
{ {
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!"); ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString()); ExplorerCore.Log(e);
} }
} }
//internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
//
//internal static string GetArgumentString(ParameterInfo[] args)
//{
// var sb = new StringBuilder();
// sb.Append(' ');
// sb.Append('(');
// foreach (var param in args)
// {
// sb.Append(param.ParameterType.Name);
// sb.Append(' ');
// sb.Append(param.Name);
// sb.Append(',');
// sb.Append(' ');
// }
// sb.Append(')');
// return sb.ToString();
//}
} }
} }

View File

@ -45,13 +45,11 @@ namespace UnityExplorer.CacheObject
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments()); ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
else else
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs); ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null; LastException = null;
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)
{ {
HadException = true;
LastException = ex; LastException = ex;
return null; return null;
} }

View File

@ -57,7 +57,6 @@ namespace UnityExplorer.CacheObject
public abstract bool ShouldAutoEvaluate { get; } public abstract bool ShouldAutoEvaluate { get; }
public abstract bool HasArguments { get; } public abstract bool HasArguments { get; }
public abstract bool CanWrite { get; } public abstract bool CanWrite { get; }
public bool HadException { get; protected set; }
public Exception LastException { get; protected set; } public Exception LastException { get; protected set; }
public virtual void SetFallbackType(Type fallbackType) public virtual void SetFallbackType(Type fallbackType)
@ -106,8 +105,8 @@ namespace UnityExplorer.CacheObject
if (CellView != null) if (CellView != null)
SetDataToCell(CellView); SetDataToCell(CellView);
// If the owner's parent CacheObject is set, we are setting the value of an inspected struct. // If the owner's ParentCacheObject is set, we are setting the value of an inspected struct.
// Set the inspector target as the value back to that parent cacheobject. // Set the inspector target as the value back to that parent.
if (Owner.ParentCacheObject != null) if (Owner.ParentCacheObject != null)
Owner.ParentCacheObject.SetUserValue(Owner.Target); Owner.ParentCacheObject.SetUserValue(Owner.Target);
} }
@ -137,7 +136,7 @@ namespace UnityExplorer.CacheObject
{ {
var prevState = State; var prevState = State;
if (HadException) if (LastException != null)
{ {
LastValueWasNull = true; LastValueWasNull = true;
LastValueType = FallbackType; LastValueType = FallbackType;
@ -158,7 +157,7 @@ namespace UnityExplorer.CacheObject
{ {
// If we changed states (always needs IValue change) // If we changed states (always needs IValue change)
// or if the value is null, and the fallback type isnt string (we always want to edit strings). // or if the value is null, and the fallback type isnt string (we always want to edit strings).
if (State != prevState || (State != ValueState.String && Value.IsNullOrDestroyed())) if (State != prevState || (State != ValueState.String && State != ValueState.Exception && Value.IsNullOrDestroyed()))
{ {
// need to return IValue // need to return IValue
ReleaseIValue(); ReleaseIValue();
@ -206,7 +205,7 @@ namespace UnityExplorer.CacheObject
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>"; return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception: case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>"; return $"<i><color=#eb4034>{LastException.ReflectionExToString()}</color></i>";
// bool and number dont want the label for the value at all // bool and number dont want the label for the value at all
case ValueState.Boolean: case ValueState.Boolean:
@ -291,36 +290,36 @@ namespace UnityExplorer.CacheObject
switch (State) switch (State)
{ {
case ValueState.Exception: case ValueState.Exception:
SetValueState(cell, ValueStateArgs.Default); SetValueState(cell, new(true, subContentButtonActive: true));
break; break;
case ValueState.Boolean: case ValueState.Boolean:
SetValueState(cell, new ValueStateArgs(false, toggleActive: true, applyActive: CanWrite)); SetValueState(cell, new(false, toggleActive: true, applyActive: CanWrite));
break; break;
case ValueState.Number: case ValueState.Number:
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite)); SetValueState(cell, new(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
break; break;
case ValueState.String: case ValueState.String:
if (LastValueWasNull) if (LastValueWasNull)
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true)); SetValueState(cell, new(true, subContentButtonActive: true));
else else
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true)); SetValueState(cell, new(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
break; break;
case ValueState.Enum: case ValueState.Enum:
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite)); SetValueState(cell, new(true, subContentButtonActive: CanWrite));
break; break;
case ValueState.Color: case ValueState.Color:
case ValueState.ValueStruct: case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType)) if (ParseUtility.CanParse(LastValueType))
SetValueState(cell, new ValueStateArgs(false, false, null, true, false, true, CanWrite, true, true)); SetValueState(cell, new(false, false, null, true, false, true, CanWrite, true, true));
else else
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true)); SetValueState(cell, new(true, inspectActive: true, subContentButtonActive: true));
break; break;
case ValueState.Collection: case ValueState.Collection:
case ValueState.Dictionary: case ValueState.Dictionary:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull)); SetValueState(cell, new(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
break; break;
case ValueState.Unsupported: case ValueState.Unsupported:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull)); SetValueState(cell, new (true, inspectActive: !LastValueWasNull));
break; break;
} }
@ -368,8 +367,10 @@ namespace UnityExplorer.CacheObject
if (cell.InspectButton != null) if (cell.InspectButton != null)
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull); cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull);
// allow IValue for null strings though // set subcontent button if needed, and for null strings and exceptions
cell.SubContentButton.Component.gameObject.SetActive(args.subContentButtonActive && (!LastValueWasNull || State == ValueState.String)); cell.SubContentButton.Component.gameObject.SetActive(
args.subContentButtonActive
&& (!LastValueWasNull || State == ValueState.String || State == ValueState.Exception));
} }
// CacheObjectCell Apply // CacheObjectCell Apply
@ -401,7 +402,7 @@ namespace UnityExplorer.CacheObject
{ {
if (this.IValue == null) if (this.IValue == null)
{ {
var ivalueType = InteractiveValue.GetIValueTypeForState(State); Type ivalueType = InteractiveValue.GetIValueTypeForState(State);
if (ivalueType == null) if (ivalueType == null)
return; return;
@ -455,6 +456,7 @@ namespace UnityExplorer.CacheObject
{ {
inactiveIValueHolder = new GameObject("Temp_IValue_Holder"); inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder); GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.hideFlags = HideFlags.HideAndDontSave;
inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform; inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform;
inactiveIValueHolder.SetActive(false); inactiveIValueHolder.SetActive(false);
} }
@ -467,9 +469,20 @@ namespace UnityExplorer.CacheObject
public struct ValueStateArgs public struct ValueStateArgs
{ {
public ValueStateArgs(bool valueActive = true, bool valueRichText = true, Color? valueColor = null, public static ValueStateArgs Default { get; } = new(true);
bool typeLabelActive = false, bool toggleActive = false, bool inputActive = false, bool applyActive = false,
bool inspectActive = false, bool subContentButtonActive = false) public Color valueColor;
public bool valueActive, valueRichText, typeLabelActive, toggleActive, inputActive, applyActive, inspectActive, subContentButtonActive;
public ValueStateArgs(bool valueActive = true,
bool valueRichText = true,
Color? valueColor = null,
bool typeLabelActive = false,
bool toggleActive = false,
bool inputActive = false,
bool applyActive = false,
bool inspectActive = false,
bool subContentButtonActive = false)
{ {
this.valueActive = valueActive; this.valueActive = valueActive;
this.valueRichText = valueRichText; this.valueRichText = valueRichText;
@ -481,14 +494,6 @@ namespace UnityExplorer.CacheObject
this.inspectActive = inspectActive; this.inspectActive = inspectActive;
this.subContentButtonActive = subContentButtonActive; this.subContentButtonActive = subContentButtonActive;
} }
public static ValueStateArgs Default => _default;
private static ValueStateArgs _default = new ValueStateArgs(true);
public bool valueActive, valueRichText, typeLabelActive, toggleActive,
inputActive, applyActive, inspectActive, subContentButtonActive;
public Color valueColor;
} }
} }
} }

View File

@ -40,13 +40,11 @@ namespace UnityExplorer.CacheObject
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments()); ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else else
ret = PropertyInfo.GetValue(DeclaringInstance, null); ret = PropertyInfo.GetValue(DeclaringInstance, null);
HadException = false;
LastException = null; LastException = null;
return ret; return ret;
} }
catch (Exception ex) catch (Exception ex)
{ {
HadException = true;
LastException = ex; LastException = ex;
return null; return null;
} }

View File

@ -31,8 +31,9 @@ namespace UnityExplorer.CacheObject.IValues
{ {
base.OnBorrowed(owner); base.OnBorrowed(owner);
inputField.Component.readOnly = !owner.CanWrite; bool canWrite = owner.CanWrite && owner.State != ValueState.Exception;
ApplyButton.Component.gameObject.SetActive(owner.CanWrite); inputField.Component.readOnly = !canWrite;
ApplyButton.Component.gameObject.SetActive(canWrite);
SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt"); SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
} }
@ -47,6 +48,9 @@ namespace UnityExplorer.CacheObject.IValues
public override void SetValue(object value) public override void SetValue(object value)
{ {
if (CurrentOwner.State == ValueState.Exception)
value = CurrentOwner.LastException.ToString();
RealValue = value as string; RealValue = value as string;
SaveFileRow.SetActive(IsStringTooLong(RealValue)); SaveFileRow.SetActive(IsStringTooLong(RealValue));

View File

@ -16,22 +16,16 @@ namespace UnityExplorer.CacheObject.IValues
{ {
public static Type GetIValueTypeForState(ValueState state) public static Type GetIValueTypeForState(ValueState state)
{ {
switch (state) return state switch
{ {
case ValueState.String: ValueState.Exception or ValueState.String => typeof(InteractiveString),
return typeof(InteractiveString); ValueState.Enum => typeof(InteractiveEnum),
case ValueState.Enum: ValueState.Collection => typeof(InteractiveList),
return typeof(InteractiveEnum); ValueState.Dictionary => typeof(InteractiveDictionary),
case ValueState.Collection: ValueState.ValueStruct => typeof(InteractiveValueStruct),
return typeof(InteractiveList); ValueState.Color => typeof(InteractiveColor),
case ValueState.Dictionary: _ => null,
return typeof(InteractiveDictionary); };
case ValueState.ValueStruct:
return typeof(InteractiveValueStruct);
case ValueState.Color:
return typeof(InteractiveColor);
default: return null;
}
} }
public GameObject UIRoot { get; set; } public GameObject UIRoot { get; set; }
@ -39,28 +33,28 @@ namespace UnityExplorer.CacheObject.IValues
public virtual bool CanWrite => this.CurrentOwner.CanWrite; public virtual bool CanWrite => this.CurrentOwner.CanWrite;
public CacheObjectBase CurrentOwner => m_owner; public CacheObjectBase CurrentOwner => owner;
private CacheObjectBase m_owner; private CacheObjectBase owner;
public bool PendingValueWanted; public bool PendingValueWanted;
public virtual void OnBorrowed(CacheObjectBase owner) public virtual void OnBorrowed(CacheObjectBase owner)
{ {
if (this.m_owner != null) if (this.owner != null)
{ {
ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?"); ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
ReleaseFromOwner(); ReleaseFromOwner();
} }
this.m_owner = owner; this.owner = owner;
} }
public virtual void ReleaseFromOwner() public virtual void ReleaseFromOwner()
{ {
if (this.m_owner == null) if (this.owner == null)
return; return;
this.m_owner = null; this.owner = null;
} }
public abstract void SetValue(object value); public abstract void SetValue(object value);

View File

@ -9,29 +9,26 @@ using UnhollowerRuntimeLib;
namespace UnityExplorer namespace UnityExplorer
{ {
// Handles all Behaviour update calls for UnityExplorer (Update, FixedUpdate, OnPostRender).
// Basically just a wrapper which calls the corresponding methods in ExplorerCore.
public class ExplorerBehaviour : MonoBehaviour public class ExplorerBehaviour : MonoBehaviour
{ {
internal static ExplorerBehaviour Instance { get; private set; } internal static ExplorerBehaviour Instance { get; private set; }
#if CPP
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
internal static void Setup() internal static void Setup()
{ {
#if CPP #if CPP
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>(); ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
#endif #endif
var obj = new GameObject("ExplorerBehaviour"); GameObject obj = new("ExplorerBehaviour");
GameObject.DontDestroyOnLoad(obj); DontDestroyOnLoad(obj);
obj.hideFlags |= HideFlags.HideAndDontSave; obj.hideFlags = HideFlags.HideAndDontSave;
Instance = obj.AddComponent<ExplorerBehaviour>(); Instance = obj.AddComponent<ExplorerBehaviour>();
} }
#if CPP
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
internal void Update() internal void Update()
{ {
ExplorerCore.Update(); ExplorerCore.Update();

View File

@ -1,10 +1,13 @@
using System.IO; using System;
using System.IO;
using UnityEngine; using UnityEngine;
using UnityExplorer.CacheObject;
using UnityExplorer.Config; using UnityExplorer.Config;
using UnityExplorer.ObjectExplorer; using UnityExplorer.ObjectExplorer;
using UnityExplorer.Runtime; using UnityExplorer.Runtime;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.Input; using UniverseLib.Input;
namespace UnityExplorer namespace UnityExplorer
@ -12,7 +15,7 @@ namespace UnityExplorer
public static class ExplorerCore public static class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "4.6.0"; public const string VERSION = "4.6.3";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
@ -26,23 +29,20 @@ namespace UnityExplorer
public static void Init(IExplorerLoader loader) public static void Init(IExplorerLoader loader)
{ {
if (Loader != null) if (Loader != null)
{ throw new Exception("UnityExplorer is already loaded.");
LogWarning("UnityExplorer is already loaded!");
return;
}
Loader = loader; Loader = loader;
Log($"{NAME} {VERSION} initializing..."); Log($"{NAME} {VERSION} initializing...");
if (!Directory.Exists(Loader.ExplorerFolder)) Directory.CreateDirectory(Loader.ExplorerFolder);
Directory.CreateDirectory(Loader.ExplorerFolder);
ConfigManager.Init(Loader.ConfigHandler); ConfigManager.Init(Loader.ConfigHandler);
UERuntimeHelper.Init(); UERuntimeHelper.Init();
ExplorerBehaviour.Setup(); ExplorerBehaviour.Setup();
UnityCrashPrevention.Init(); UnityCrashPrevention.Init();
UniverseLib.Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new() Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new()
{ {
Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value, Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value,
Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value, Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value,
@ -53,7 +53,7 @@ namespace UnityExplorer
// Do a delayed setup so that objects aren't destroyed instantly. // Do a delayed setup so that objects aren't destroyed instantly.
// This can happen for a multitude of reasons. // This can happen for a multitude of reasons.
// Default delay is 1 second which is usually enough. // Default delay is 1 second which is usually enough.
private static void LateInit() static void LateInit()
{ {
Log($"Setting up late core features..."); Log($"Setting up late core features...");
@ -63,21 +63,18 @@ namespace UnityExplorer
UIManager.InitUI(); UIManager.InitUI();
Log($"{NAME} {VERSION} initialized for {UniverseLib.Universe.Context}."); Log($"{NAME} {VERSION} ({Universe.Context}) initialized.");
//InspectorManager.Inspect(typeof(Tests.TestClass)); //InspectorManager.Inspect(typeof(Tests.TestClass));
} }
/// <summary> internal static void Update()
/// Should be called once per frame.
/// </summary>
public static void Update()
{ {
UIManager.Update();
// check master toggle // check master toggle
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value)) if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
UIManager.ShowMenu = !UIManager.ShowMenu; UIManager.ShowMenu = !UIManager.ShowMenu;
UIManager.Update();
} }
#region LOGGING #region LOGGING

View File

@ -160,11 +160,12 @@ namespace UnityExplorer.Inspectors
// Unity object helper widget // Unity object helper widget
this.UnityWidget = UnityObjectWidget.GetUnityWidget(target, TargetType, this); if (!StaticOnly)
this.UnityWidget = UnityObjectWidget.GetUnityWidget(target, TargetType, this);
// Get cache members // Get cache members
this.members = CacheMemberFactory.GetCacheMembers(Target, TargetType, this); this.members = CacheMemberFactory.GetCacheMembers(TargetType, this);
// reset filters // reset filters

View File

@ -73,6 +73,10 @@ namespace UnityExplorer.Loader.Standalone
return bool.Parse(value); return bool.Parse(value);
else if (elementType == typeof(int)) else if (elementType == typeof(int))
return int.Parse(value); return int.Parse(value);
else if (elementType == typeof(float))
return float.Parse(value);
else if (elementType.IsEnum)
return Enum.Parse(elementType, value);
else else
return value; return value;
} }

View File

@ -26,7 +26,7 @@ namespace UnityExplorer.ObjectExplorer
private static Scene? selectedScene; private static Scene? selectedScene;
/// <summary>The GameObjects in the currently inspected scene.</summary> /// <summary>The GameObjects in the currently inspected scene.</summary>
public static GameObject[] CurrentRootObjects { get; private set; } = new GameObject[0]; public static IEnumerable<GameObject> CurrentRootObjects { get; private set; } = new GameObject[0];
/// <summary>All currently loaded Scenes.</summary> /// <summary>All currently loaded Scenes.</summary>
public static List<Scene> LoadedScenes { get; private set; } = new(); public static List<Scene> LoadedScenes { get; private set; } = new();
@ -129,7 +129,7 @@ namespace UnityExplorer.ObjectExplorer
if (go.transform.parent == null && !go.scene.IsValid()) if (go.transform.parent == null && !go.scene.IsValid())
objects.Add(go); objects.Add(go);
} }
CurrentRootObjects = objects.ToArray(); CurrentRootObjects = objects;
} }
} }
} }

View File

@ -24,8 +24,12 @@ namespace UnityExplorer.Tests
#endif #endif
} }
#region MONO
public static object LiterallyAnything = null; public static object LiterallyAnything = null;
public static string Exception => throw new Exception("This is a test.");
// Test enumerables // Test enumerables
public static int[,,] MultiDimensionalArray = new int[45, 45, 45]; public static int[,,] MultiDimensionalArray = new int[45, 45, 45];
public static List<object> ListOfInts; public static List<object> ListOfInts;
@ -145,6 +149,8 @@ namespace UnityExplorer.Tests
ExplorerCore.Log("Finished TestClass Init_Mono"); ExplorerCore.Log("Finished TestClass Init_Mono");
} }
#endregion
#if CPP #if CPP
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict; public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet; public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
@ -257,6 +263,7 @@ namespace UnityExplorer.Tests
ExplorerCore.Log($"Finished Init_Il2Cpp"); ExplorerCore.Log($"Finished Init_Il2Cpp");
} }
#endif #endif
} }
} }

View File

@ -88,7 +88,7 @@ namespace UnityExplorer.UI
DisplayManager.Init(); DisplayManager.Init();
var display = DisplayManager.ActiveDisplay; Display display = DisplayManager.ActiveDisplay;
lastScreenWidth = display.renderingWidth; lastScreenWidth = display.renderingWidth;
lastScreenHeight = display.renderingHeight; lastScreenHeight = display.renderingHeight;

View File

@ -10,7 +10,7 @@ namespace UnityExplorer.UI.Widgets
{ {
public TransformTree Tree { get; } public TransformTree Tree { get; }
public Transform Value { get; private set; } public Transform Value { get; private set; }
public int InstanceID { get; private set; } public int InstanceID { get; }
public CachedTransform Parent { get; internal set; } public CachedTransform Parent { get; internal set; }
public int Depth { get; internal set; } public int Depth { get; internal set; }
@ -23,10 +23,11 @@ namespace UnityExplorer.UI.Widgets
public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null) public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
{ {
InstanceID = transform.GetInstanceID();
Tree = tree; Tree = tree;
Value = transform; Value = transform;
Parent = parent; Parent = parent;
InstanceID = transform.GetInstanceID();
SiblingIndex = transform.GetSiblingIndex(); SiblingIndex = transform.GetSiblingIndex();
Update(transform, depth); Update(transform, depth);
} }

View File

@ -29,7 +29,6 @@ namespace UnityExplorer.UI.Widgets
public Action<GameObject> OnGameObjectClicked; public Action<GameObject> OnGameObjectClicked;
public CachedTransform cachedTransform; public CachedTransform cachedTransform;
public int cellIndex;
public GameObject UIRoot { get; set; } public GameObject UIRoot { get; set; }
public RectTransform Rect { get; set; } public RectTransform Rect { get; set; }
@ -53,7 +52,7 @@ namespace UnityExplorer.UI.Widgets
UIRoot.SetActive(false); UIRoot.SetActive(false);
} }
public void ConfigureCell(CachedTransform cached, int cellIndex) public void ConfigureCell(CachedTransform cached)
{ {
if (cached == null) if (cached == null)
{ {
@ -64,7 +63,6 @@ namespace UnityExplorer.UI.Widgets
if (!Enabled) if (!Enabled)
Enable(); Enable();
this.cellIndex = cellIndex;
cachedTransform = cached; cachedTransform = cached;
spacer.minWidth = cached.Depth * 15; spacer.minWidth = cached.Depth * 15;

View File

@ -22,8 +22,8 @@ namespace UnityExplorer.UI.Widgets
// - Remove(object) // - Remove(object)
// - set_Item[object] // - set_Item[object]
// These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary. // These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary.
// Currently we do not use either of these methods, so everything should be constant time hash lookups. // Currently we do not use either of these methods, so everything should be constant time lookups.
// We DO make use of get_Item[object], get_Item[index], Add, Insert and RemoveAt, which OrderedDictionary perfectly meets our needs for. // We DO make use of get_Item[object], get_Item[index], Add, Insert, Contains and RemoveAt, which OrderedDictionary meets our needs for.
/// <summary> /// <summary>
/// Key: UnityEngine.Transform instance ID<br/> /// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform /// Value: CachedTransform
@ -182,25 +182,25 @@ namespace UnityExplorer.UI.Widgets
traversedThisFrame.Reset(); traversedThisFrame.Reset();
traversedThisFrame.Start(); traversedThisFrame.Start();
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod.Invoke(); refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(andRefreshUI, jumpToTop, oneShot));
refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(rootObjects, andRefreshUI, jumpToTop, oneShot));
} }
// Coroutine for batched updates, max 2000 gameobjects per frame so FPS doesn't get tanked when there is like 100k gameobjects. IEnumerator RefreshCoroutine(bool andRefreshUI, bool jumpToTop, bool oneShot)
// if "oneShot", then this will NOT be batched (if we need an immediate full update).
IEnumerator RefreshCoroutine(IEnumerable<GameObject> rootObjects, bool andRefreshUI, bool jumpToTop, bool oneShot)
{ {
// Instead of doing string.IsNullOrEmpty(CurrentFilter) many times, let's just do it once per update.
bool filtering = Filtering;
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod();
foreach (var gameObj in rootObjects) foreach (var gameObj in rootObjects)
{ {
if (gameObj) if (!gameObj)
continue;
IEnumerator enumerator = Traverse(gameObj.transform, null, 0, oneShot, filtering);
while (enumerator.MoveNext())
{ {
var enumerator = Traverse(gameObj.transform, null, 0, oneShot); if (!oneShot)
while (enumerator.MoveNext()) yield return enumerator.Current;
{
if (!oneShot)
yield return enumerator.Current;
}
} }
} }
@ -224,7 +224,7 @@ namespace UnityExplorer.UI.Widgets
// Recursive method to check a Transform and its children (if expanded). // Recursive method to check a Transform and its children (if expanded).
// Parent and depth can be null/default. // Parent and depth can be null/default.
private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot) private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot, bool filtering)
{ {
// Let's only tank 2ms of each frame (60->53fps) // Let's only tank 2ms of each frame (60->53fps)
if (traversedThisFrame.ElapsedMilliseconds > 2) if (traversedThisFrame.ElapsedMilliseconds > 2)
@ -236,21 +236,20 @@ namespace UnityExplorer.UI.Widgets
int instanceID = transform.GetInstanceID(); int instanceID = transform.GetInstanceID();
// Unlikely, but since this method is async it could theoretically happen in extremely rare circumstances
if (visited.Contains(instanceID)) if (visited.Contains(instanceID))
yield break; yield break;
if (Filtering) if (filtering)
{ {
if (!FilterHierarchy(transform)) if (!FilterHierarchy(transform))
yield break; yield break;
visited.Add(instanceID);
if (!autoExpandedIDs.Contains(instanceID)) if (!autoExpandedIDs.Contains(instanceID))
autoExpandedIDs.Add(instanceID); autoExpandedIDs.Add(instanceID);
} }
else
visited.Add(instanceID); visited.Add(instanceID);
CachedTransform cached; CachedTransform cached;
if (cachedTransforms.Contains(instanceID)) if (cachedTransforms.Contains(instanceID))
@ -286,9 +285,11 @@ namespace UnityExplorer.UI.Widgets
if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0) if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0)
{ {
ExplorerCore.Log($"Traversing expanded transform {cached.Value.name} ({cached.InstanceID})");
for (int i = 0; i < transform.childCount; i++) for (int i = 0; i < transform.childCount; i++)
{ {
var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot); var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot, filtering);
while (enumerator.MoveNext()) while (enumerator.MoveNext())
{ {
if (!oneShot) if (!oneShot)
@ -317,7 +318,7 @@ namespace UnityExplorer.UI.Widgets
{ {
if (index < cachedTransforms.Count) if (index < cachedTransforms.Count)
{ {
cell.ConfigureCell((CachedTransform)cachedTransforms[index], index); cell.ConfigureCell((CachedTransform)cachedTransforms[index]);
if (Filtering) if (Filtering)
{ {
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter)) if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
@ -345,6 +346,8 @@ namespace UnityExplorer.UI.Widgets
public void OnCellExpandToggled(CachedTransform cache) public void OnCellExpandToggled(CachedTransform cache)
{ {
ExplorerCore.Log($"OnCellExpandToggled: {cache.Value.name} ({cache.InstanceID})");
var instanceID = cache.InstanceID; var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID)) if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID); expandedInstanceIDs.Remove(instanceID);

View File

@ -28,17 +28,16 @@ namespace UnityExplorer.UI.Widgets
if (!typeof(UnityEngine.Object).IsAssignableFrom(targetType)) if (!typeof(UnityEngine.Object).IsAssignableFrom(targetType))
return null; return null;
UnityObjectWidget ret; UnityObjectWidget widget = target switch
{
Texture2D => Pool<Texture2DWidget>.Borrow(),
AudioClip => Pool<AudioClipWidget>.Borrow(),
_ => Pool<UnityObjectWidget>.Borrow()
};
if (targetType == typeof(Texture2D)) widget.OnBorrowed(target, targetType, inspector);
ret = Pool<Texture2DWidget>.Borrow();
else if (targetType == typeof(AudioClip))
ret = Pool<AudioClipWidget>.Borrow();
else
ret = Pool<UnityObjectWidget>.Borrow();
ret.OnBorrowed(target, targetType, inspector); return widget;
return ret;
} }
public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector) public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
@ -52,7 +51,7 @@ namespace UnityExplorer.UI.Widgets
this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2); this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
UnityObjectRef = (UnityEngine.Object)target.TryCast(typeof(UnityEngine.Object)); UnityObjectRef = target.TryCast<UnityEngine.Object>();
UIRoot.SetActive(true); UIRoot.SetActive(true);
nameInput.Text = UnityObjectRef.name; nameInput.Text = UnityObjectRef.name;

View File

@ -175,13 +175,13 @@
<Private>False</Private> <Private>False</Private>
</Reference> </Reference>
<Reference Include="UniverseLib.Mono"> <Reference Include="UniverseLib.Mono">
<HintPath>packages\UniverseLib.1.2.16\lib\net35\UniverseLib.Mono.dll</HintPath> <HintPath>packages\UniverseLib.1.2.18\lib\net35\UniverseLib.Mono.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<!-- Il2Cpp refs --> <!-- Il2Cpp refs -->
<ItemGroup Condition="'$(IsCpp)'=='true'"> <ItemGroup Condition="'$(IsCpp)'=='true'">
<Reference Include="UniverseLib.IL2CPP"> <Reference Include="UniverseLib.IL2CPP">
<HintPath>packages\UniverseLib.1.2.16\lib\net472\UniverseLib.IL2CPP.dll</HintPath> <HintPath>packages\UniverseLib.1.2.18\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
</Reference> </Reference>
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath> <HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>

View File

@ -6,6 +6,6 @@
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" /> <package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" /> <package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" /> <package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
<package id="UniverseLib" version="1.2.16" targetFramework="net35" /> <package id="UniverseLib" version="1.2.18" targetFramework="net35" />
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" /> <package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
</packages> </packages>