mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 22:27:45 +08:00
Implemented the console log, some cleanups
This commit is contained in:
parent
89022db5fc
commit
12fe19ba8e
@ -108,9 +108,6 @@ namespace UnityExplorer.Core.Config
|
|||||||
"\r\nSeperate signatures with a semicolon ';'.",
|
"\r\nSeperate signatures with a semicolon ';'.",
|
||||||
"DEFAULT");
|
"DEFAULT");
|
||||||
|
|
||||||
Reflection_Signature_Blacklist.OnValueChanged += ReflectionUtility.LoadBlacklistString;
|
|
||||||
ReflectionUtility.LoadBlacklistString(Reflection_Signature_Blacklist.Value);
|
|
||||||
|
|
||||||
// Internal configs (panel save data)
|
// Internal configs (panel save data)
|
||||||
|
|
||||||
ObjectExplorerData = new ConfigElement<string>("ObjectExplorer", "", "", true);
|
ObjectExplorerData = new ConfigElement<string>("ObjectExplorer", "", "", true);
|
||||||
|
@ -14,13 +14,16 @@ using System.Diagnostics.CodeAnalysis;
|
|||||||
using UnityExplorer.Core;
|
using UnityExplorer.Core;
|
||||||
using CppType = Il2CppSystem.Type;
|
using CppType = Il2CppSystem.Type;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
|
using UnityExplorer.Core.Config;
|
||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
public class Il2CppReflection : ReflectionUtility
|
public class Il2CppReflection : ReflectionUtility
|
||||||
{
|
{
|
||||||
public Il2CppReflection()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
TryLoadGameModules();
|
TryLoadGameModules();
|
||||||
|
|
||||||
BuildDeobfuscationCache();
|
BuildDeobfuscationCache();
|
||||||
@ -70,7 +73,7 @@ namespace UnityExplorer
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (DeobfuscatedTypes.Count > 0)
|
if (DeobfuscatedTypes.Count > 0)
|
||||||
ExplorerCore.Log($"Built deobfuscation cache, count: {DeobfuscatedTypes.Count}");
|
ExplorerCore.Log($"Built IL2CPP deobfuscation cache, initial count: {DeobfuscatedTypes.Count}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void TryCacheDeobfuscatedType(Type type)
|
private static void TryCacheDeobfuscatedType(Type type)
|
||||||
|
@ -17,16 +17,25 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
|
||||||
|
|
||||||
internal static readonly ReflectionUtility Instance =
|
internal static ReflectionUtility Instance;
|
||||||
|
|
||||||
|
public static void Init()
|
||||||
|
{
|
||||||
|
Instance =
|
||||||
#if CPP
|
#if CPP
|
||||||
new Il2CppReflection();
|
new Il2CppReflection();
|
||||||
#else
|
#else
|
||||||
new ReflectionUtility();
|
new ReflectionUtility();
|
||||||
#endif
|
#endif
|
||||||
|
Instance.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
static ReflectionUtility()
|
protected virtual void Initialize()
|
||||||
{
|
{
|
||||||
SetupTypeCache();
|
SetupTypeCache();
|
||||||
|
|
||||||
|
LoadBlacklistString(ConfigManager.Reflection_Signature_Blacklist.Value);
|
||||||
|
ConfigManager.Reflection_Signature_Blacklist.OnValueChanged += LoadBlacklistString;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Type cache
|
#region Type cache
|
||||||
@ -409,7 +418,12 @@ namespace UnityExplorer
|
|||||||
public static void LoadBlacklistString(string blacklist)
|
public static void LoadBlacklistString(string blacklist)
|
||||||
{
|
{
|
||||||
if (string.Equals(blacklist, "DEFAULT", StringComparison.InvariantCultureIgnoreCase))
|
if (string.Equals(blacklist, "DEFAULT", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
{
|
||||||
blacklist = Instance.DefaultReflectionBlacklist;
|
blacklist = Instance.DefaultReflectionBlacklist;
|
||||||
|
ConfigManager.Reflection_Signature_Blacklist.Value = blacklist;
|
||||||
|
ConfigManager.Handler.SaveConfig();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(blacklist))
|
if (string.IsNullOrEmpty(blacklist))
|
||||||
return;
|
return;
|
||||||
|
@ -8,21 +8,17 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
public static class IOUtility
|
public static class IOUtility
|
||||||
{
|
{
|
||||||
public static string EnsureValid(string path)
|
private static readonly char[] invalidDirectoryCharacters = Path.GetInvalidPathChars();
|
||||||
|
private static readonly char[] invalidFilenameCharacters = Path.GetInvalidFileNameChars();
|
||||||
|
|
||||||
|
public static string EnsureValidDirectory(string path)
|
||||||
{
|
{
|
||||||
path = RemoveInvalidChars(path);
|
return string.Concat(path.Split(invalidDirectoryCharacters));
|
||||||
|
|
||||||
var dir = Path.GetDirectoryName(path);
|
|
||||||
|
|
||||||
if (!Directory.Exists(dir))
|
|
||||||
Directory.CreateDirectory(dir);
|
|
||||||
|
|
||||||
return path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string RemoveInvalidChars(string path)
|
public static string EnsureValidFilename(string filename)
|
||||||
{
|
{
|
||||||
return string.Concat(path.Split(Path.GetInvalidPathChars()));
|
return string.Concat(filename.Split(invalidFilenameCharacters));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,8 @@ namespace UnityExplorer
|
|||||||
}
|
}
|
||||||
Loader = loader;
|
Loader = loader;
|
||||||
|
|
||||||
|
Log($"{NAME} {VERSION} initializing...");
|
||||||
|
|
||||||
ExplorerBehaviour.Setup();
|
ExplorerBehaviour.Setup();
|
||||||
|
|
||||||
if (!Directory.Exists(Loader.ExplorerFolder))
|
if (!Directory.Exists(Loader.ExplorerFolder))
|
||||||
@ -45,13 +47,15 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
ConfigManager.Init(Loader.ConfigHandler);
|
ConfigManager.Init(Loader.ConfigHandler);
|
||||||
|
|
||||||
|
ReflectionUtility.Init();
|
||||||
|
|
||||||
RuntimeProvider.Init();
|
RuntimeProvider.Init();
|
||||||
SceneHandler.Init();
|
SceneHandler.Init();
|
||||||
InputManager.Init();
|
InputManager.Init();
|
||||||
|
|
||||||
Log($"{NAME} {VERSION} initialized.");
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.StartCoroutine(SetupCoroutine());
|
RuntimeProvider.Instance.StartCoroutine(SetupCoroutine());
|
||||||
|
|
||||||
|
Log($"Finished core setup, waiting for UI setup...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do a delayed setup so that objects aren't destroyed instantly.
|
// Do a delayed setup so that objects aren't destroyed instantly.
|
||||||
@ -71,11 +75,11 @@ namespace UnityExplorer
|
|||||||
yield return null;
|
yield return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log($"Creating UI, after delay of {ConfigManager.Startup_Delay_Time.Value} second(s).");
|
Log($"Creating UI...");
|
||||||
|
|
||||||
UIManager.InitUI();
|
UIManager.InitUI();
|
||||||
|
|
||||||
// END
|
Log($"{NAME} {VERSION} initialized.");
|
||||||
|
|
||||||
//InspectorManager.Inspect(typeof(TestClass));
|
//InspectorManager.Inspect(typeof(TestClass));
|
||||||
}
|
}
|
||||||
@ -123,23 +127,22 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
string log = message?.ToString() ?? "";
|
string log = message?.ToString() ?? "";
|
||||||
|
|
||||||
|
LogPanel.Log(log, logType);
|
||||||
|
|
||||||
switch (logType)
|
switch (logType)
|
||||||
{
|
{
|
||||||
case LogType.Assert:
|
case LogType.Assert:
|
||||||
case LogType.Log:
|
case LogType.Log:
|
||||||
Loader.OnLogMessage(log);
|
Loader.OnLogMessage(log);
|
||||||
//DebugConsole.Log(log, Color.white);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Warning:
|
case LogType.Warning:
|
||||||
Loader.OnLogWarning(log);
|
Loader.OnLogWarning(log);
|
||||||
//DebugConsole.Log(log, Color.yellow);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Error:
|
case LogType.Error:
|
||||||
case LogType.Exception:
|
case LogType.Exception:
|
||||||
Loader.OnLogError(log);
|
Loader.OnLogError(log);
|
||||||
//DebugConsole.Log(log, Color.red);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,47 +16,6 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
{
|
{
|
||||||
public static class ConsoleController
|
public static class ConsoleController
|
||||||
{
|
{
|
||||||
#region Strings / defaults
|
|
||||||
|
|
||||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
|
||||||
|
|
||||||
The following helper methods are available:
|
|
||||||
|
|
||||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
|
||||||
|
|
||||||
* <color=#add490>CurrentTarget()</color> returns the target of the active Inspector tab as System.Object
|
|
||||||
|
|
||||||
* <color=#add490>AllTargets()</color> returns a System.Object[] array containing the targets of all active tabs
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
|
||||||
|
|
||||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
|
||||||
|
|
||||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
|
||||||
";
|
|
||||||
|
|
||||||
internal static readonly string[] DefaultUsing = new string[]
|
|
||||||
{
|
|
||||||
"System",
|
|
||||||
"System.Linq",
|
|
||||||
"System.Collections",
|
|
||||||
"System.Collections.Generic",
|
|
||||||
"System.Reflection",
|
|
||||||
"UnityEngine",
|
|
||||||
#if CPP
|
|
||||||
"UnhollowerBaseLib",
|
|
||||||
"UnhollowerRuntimeLib",
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public static ScriptEvaluator Evaluator;
|
public static ScriptEvaluator Evaluator;
|
||||||
public static LexerBuilder Lexer;
|
public static LexerBuilder Lexer;
|
||||||
public static CSAutoCompleter Completer;
|
public static CSAutoCompleter Completer;
|
||||||
@ -75,16 +34,34 @@ The following helper methods are available:
|
|||||||
public static bool EnableAutoIndent { get; private set; } = true;
|
public static bool EnableAutoIndent { get; private set; } = true;
|
||||||
public static bool EnableSuggestions { get; private set; } = true;
|
public static bool EnableSuggestions { get; private set; } = true;
|
||||||
|
|
||||||
|
internal static readonly string[] DefaultUsing = new string[]
|
||||||
|
{
|
||||||
|
"System",
|
||||||
|
"System.Linq",
|
||||||
|
"System.Collections",
|
||||||
|
"System.Collections.Generic",
|
||||||
|
"System.Reflection",
|
||||||
|
"UnityEngine",
|
||||||
|
#if CPP
|
||||||
|
"UnhollowerBaseLib",
|
||||||
|
"UnhollowerRuntimeLib",
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
public static void Init()
|
public static void Init()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ResetConsole(false);
|
ResetConsole(false);
|
||||||
|
// ensure the compiler is supported (if this fails then SRE is probably stubbed)
|
||||||
Evaluator.Compile("0 == 0");
|
Evaluator.Compile("0 == 0");
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("C# Console probably not supported, todo");
|
ExplorerCore.LogWarning("C# Console is not supported, System.Reflection.Emit was probably stubbed! You can use UnityDoorstop to patch this.");
|
||||||
|
var navButton = Panel.NavButton;
|
||||||
|
navButton.Component.interactable = false;
|
||||||
|
navButton.ButtonText.text += "(disabled)";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,40 +10,37 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
{
|
{
|
||||||
public class ScriptInteraction : InteractiveBase
|
public class ScriptInteraction : InteractiveBase
|
||||||
{
|
{
|
||||||
|
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
||||||
|
|
||||||
|
Use this console to declare temporary C# classes, or evaluate standalone expressions as though you were executing a method body. "+
|
||||||
|
@"Use the <b>Reset</b> button to clear all classes and variables, and restore the default using directives.
|
||||||
|
|
||||||
|
* <color=#add490>using SomeNamespace;</color> to add a namespace to the Console. This is not required if you use full namespaces to access classes.
|
||||||
|
|
||||||
|
When evaluating standalone expressions, these helpers are available:
|
||||||
|
|
||||||
|
* System.Object <color=#96b570>CurrentTarget</color> - the target of the active Inspector tab
|
||||||
|
|
||||||
|
* System.Object[] <color=#96b570>AllTargets</color> - an array containing the targets of all Inspector tabs
|
||||||
|
|
||||||
|
* void <color=#add490>Log(""message"")</color> - logs a message to the console log
|
||||||
|
|
||||||
|
* void <color=#add490>Inspect(someObject)</color> - inspect an instance, eg. Inspect(Camera.main);
|
||||||
|
|
||||||
|
* void <color=#add490>Inspect(typeof(SomeClass))</color> - inspect a Class with static reflection
|
||||||
|
|
||||||
|
* void <color=#add490>StartCoroutine(ienumerator)</color> - start the IEnumerator as a Coroutine
|
||||||
|
|
||||||
|
";
|
||||||
|
|
||||||
public static void Log(object message)
|
public static void Log(object message)
|
||||||
{
|
{
|
||||||
ExplorerCore.Log(message);
|
ExplorerCore.Log(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StartCoroutine(IEnumerator ienumerator)
|
public static object CurrentTarget => InspectorManager.ActiveInspector?.Target;
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddUsing(string directive)
|
public static object[] AllTargets() => InspectorManager.Inspectors.Select(it => it.Target).ToArray();
|
||||||
{
|
|
||||||
ConsoleController.AddUsing(directive);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void GetUsing()
|
|
||||||
{
|
|
||||||
ExplorerCore.Log(ConsoleController.Evaluator.GetUsing());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Reset()
|
|
||||||
{
|
|
||||||
ConsoleController.ResetConsole();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object CurrentTarget()
|
|
||||||
{
|
|
||||||
return InspectorManager.ActiveInspector?.Target;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[] AllTargets()
|
|
||||||
{
|
|
||||||
return InspectorManager.Inspectors.Select(it => it.Target).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Inspect(object obj)
|
public static void Inspect(object obj)
|
||||||
{
|
{
|
||||||
@ -54,5 +51,10 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
{
|
{
|
||||||
InspectorManager.Inspect(type);
|
InspectorManager.Inspect(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void StartCoroutine(IEnumerator ienumerator)
|
||||||
|
{
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -33,6 +33,9 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
SetValueFromSource(RefConfigElement.BoxedValue);
|
SetValueFromSource(RefConfigElement.BoxedValue);
|
||||||
|
|
||||||
|
if (this.CellView != null)
|
||||||
|
this.SetDataToCell(CellView);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void TrySetUserValue(object value)
|
public override void TrySetUserValue(object value)
|
||||||
|
@ -256,7 +256,7 @@ namespace UnityExplorer.UI.CacheObject
|
|||||||
case MemberTypes.Method:
|
case MemberTypes.Method:
|
||||||
{
|
{
|
||||||
var mi = member as MethodInfo;
|
var mi = member as MethodInfo;
|
||||||
if (!ignorePropertyMethodInfos
|
if (ignorePropertyMethodInfos
|
||||||
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
|
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ namespace UnityExplorer.UI.IValues
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var path = IOUtility.EnsureValid(SaveFilePath.Text);
|
var path = IOUtility.EnsureValidDirectory(SaveFilePath.Text);
|
||||||
|
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
|
@ -665,7 +665,7 @@ namespace UnityExplorer.UI.Inspectors
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
path = IOUtility.EnsureValid(path);
|
path = IOUtility.EnsureValidDirectory(path);
|
||||||
|
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
File.Delete(path);
|
File.Delete(path);
|
||||||
|
@ -102,7 +102,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, "IndentToggle", out var AutoIndentToggle, out Text autoIndentToggleText);
|
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, "IndentToggle", out var AutoIndentToggle, out Text autoIndentToggleText);
|
||||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
||||||
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
||||||
autoIndentToggleText.text = "Auto-indent on Enter";
|
autoIndentToggleText.text = "Auto-indent";
|
||||||
AutoIndentToggle.onValueChanged.AddListener((bool val) => { OnAutoIndentToggled?.Invoke(val); });
|
AutoIndentToggle.onValueChanged.AddListener((bool val) => { OnAutoIndentToggled?.Invoke(val); });
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
@ -111,7 +111,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
int fontSize = 16;
|
int fontSize = 16;
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", ConsoleController.STARTUP_TEXT, out var inputScroller, fontSize);
|
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", ScriptInteraction.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||||
InputScroll = inputScroller;
|
InputScroll = inputScroller;
|
||||||
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||||
Input.OnValueChanged += InvokeOnValueChanged;
|
Input.OnValueChanged += InvokeOnValueChanged;
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Panels
|
|
||||||
{
|
|
||||||
public class ConsoleLogPanel : UIPanel
|
|
||||||
{
|
|
||||||
public override string Name => "Console Log";
|
|
||||||
public override UIManager.Panels PanelType => UIManager.Panels.ConsoleLog;
|
|
||||||
|
|
||||||
public override int MinWidth => 300;
|
|
||||||
public override int MinHeight => 75;
|
|
||||||
|
|
||||||
public override string GetSaveDataFromConfigManager()
|
|
||||||
{
|
|
||||||
return ConfigManager.ConsoleLogData.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void DoSaveToConfigElement()
|
|
||||||
{
|
|
||||||
ConfigManager.ConsoleLogData.Value = this.ToSaveData();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected internal override void DoSetDefaultPosAndAnchors()
|
|
||||||
{
|
|
||||||
mainPanelRect.localPosition = Vector2.zero;
|
|
||||||
mainPanelRect.pivot = new Vector2(0f, 1f);
|
|
||||||
mainPanelRect.anchorMin = new Vector2(0.5f, 0.1f);
|
|
||||||
mainPanelRect.anchorMax = new Vector2(0.9f, 0.25f);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructPanelContent()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
243
src/UI/Panels/LogPanel.cs
Normal file
243
src/UI/Panels/LogPanel.cs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Core.Config;
|
||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Panels
|
||||||
|
{
|
||||||
|
public class LogPanel : UIPanel, ICellPoolDataSource<ConsoleLogCell>
|
||||||
|
{
|
||||||
|
public struct LogInfo
|
||||||
|
{
|
||||||
|
public string message;
|
||||||
|
public LogType type;
|
||||||
|
|
||||||
|
public LogInfo(string message, LogType type) { this.message = message; this.type = type; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static readonly List<LogInfo> Logs = new List<LogInfo>();
|
||||||
|
private static string CurrentStreamPath;
|
||||||
|
|
||||||
|
public override string Name => "Log";
|
||||||
|
public override UIManager.Panels PanelType => UIManager.Panels.ConsoleLog;
|
||||||
|
|
||||||
|
public override int MinWidth => 300;
|
||||||
|
public override int MinHeight => 75;
|
||||||
|
|
||||||
|
public int ItemCount => Logs.Count;
|
||||||
|
|
||||||
|
private static ScrollPool<ConsoleLogCell> logScrollPool;
|
||||||
|
|
||||||
|
public LogPanel()
|
||||||
|
{
|
||||||
|
SetupIO();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool DoneScrollPoolInit;
|
||||||
|
|
||||||
|
public override void SetActive(bool active)
|
||||||
|
{
|
||||||
|
base.SetActive(active);
|
||||||
|
|
||||||
|
if (active && !DoneScrollPoolInit)
|
||||||
|
{
|
||||||
|
LayoutRebuilder.ForceRebuildLayoutImmediate(this.mainPanelRect);
|
||||||
|
logScrollPool.Initialize(this);
|
||||||
|
DoneScrollPoolInit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupIO()
|
||||||
|
{
|
||||||
|
var path = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Logs");
|
||||||
|
path = IOUtility.EnsureValidDirectory(path);
|
||||||
|
|
||||||
|
// clean old log(s)
|
||||||
|
var files = Directory.GetFiles(path);
|
||||||
|
if (files.Length >= 10)
|
||||||
|
{
|
||||||
|
var sorted = files.ToList();
|
||||||
|
// sort by 'datetime.ToString("u")' will put the oldest ones first
|
||||||
|
sorted.Sort();
|
||||||
|
for (int i = 0; i < files.Length - 9; i++)
|
||||||
|
File.Delete(files[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fileName = $"UnityExplorer {DateTime.Now:u}.txt";
|
||||||
|
fileName = IOUtility.EnsureValidFilename(fileName);
|
||||||
|
|
||||||
|
CurrentStreamPath = Path.Combine(path, fileName);
|
||||||
|
|
||||||
|
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logging
|
||||||
|
|
||||||
|
public static void Log(string message, LogType type)
|
||||||
|
{
|
||||||
|
Logs.Add(new LogInfo(message, type));
|
||||||
|
|
||||||
|
if (CurrentStreamPath != null)
|
||||||
|
File.AppendAllText(CurrentStreamPath, '\n' + message);
|
||||||
|
|
||||||
|
if (logScrollPool != null)
|
||||||
|
logScrollPool.Refresh(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ClearLogs()
|
||||||
|
{
|
||||||
|
Logs.Clear();
|
||||||
|
logScrollPool.Refresh(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OpenLogFile()
|
||||||
|
{
|
||||||
|
if (File.Exists(CurrentStreamPath))
|
||||||
|
Process.Start(CurrentStreamPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cell pool
|
||||||
|
|
||||||
|
private static readonly Dictionary<LogType, Color> logColors = new Dictionary<LogType, Color>
|
||||||
|
{
|
||||||
|
{ LogType.Log, Color.white },
|
||||||
|
{ LogType.Warning, Color.yellow },
|
||||||
|
{ LogType.Assert, Color.yellow },
|
||||||
|
{ LogType.Error, Color.red },
|
||||||
|
{ LogType.Exception, Color.red },
|
||||||
|
};
|
||||||
|
|
||||||
|
private readonly Color logEvenColor = new Color(0.34f, 0.34f, 0.34f);
|
||||||
|
private readonly Color logOddColor = new Color(0.28f, 0.28f, 0.28f);
|
||||||
|
|
||||||
|
public void OnCellBorrowed(ConsoleLogCell cell) { }
|
||||||
|
|
||||||
|
public void SetCell(ConsoleLogCell cell, int index)
|
||||||
|
{
|
||||||
|
if (index >= Logs.Count)
|
||||||
|
{
|
||||||
|
cell.Disable();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logs are displayed in reverse order (newest at top)
|
||||||
|
index = Logs.Count - index - 1;
|
||||||
|
|
||||||
|
var log = Logs[index];
|
||||||
|
cell.IndexLabel.text = $"{index}:";
|
||||||
|
cell.Input.Text = log.message;
|
||||||
|
cell.Input.Component.textComponent.color = logColors[log.type];
|
||||||
|
|
||||||
|
var color = index % 2 == 0 ? logEvenColor : logOddColor;
|
||||||
|
RuntimeProvider.Instance.SetColorBlock(cell.Input.Component, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel save data
|
||||||
|
|
||||||
|
public override string GetSaveDataFromConfigManager()
|
||||||
|
{
|
||||||
|
return ConfigManager.ConsoleLogData.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void DoSaveToConfigElement()
|
||||||
|
{
|
||||||
|
ConfigManager.ConsoleLogData.Value = this.ToSaveData();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected internal override void DoSetDefaultPosAndAnchors()
|
||||||
|
{
|
||||||
|
mainPanelRect.localPosition = Vector2.zero;
|
||||||
|
mainPanelRect.pivot = new Vector2(0f, 1f);
|
||||||
|
mainPanelRect.anchorMin = new Vector2(0.5f, 0.1f);
|
||||||
|
mainPanelRect.anchorMax = new Vector2(0.9f, 0.25f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Construction
|
||||||
|
|
||||||
|
public override void ConstructPanelContent()
|
||||||
|
{
|
||||||
|
// Log scroll pool
|
||||||
|
|
||||||
|
logScrollPool = UIFactory.CreateScrollPool<ConsoleLogCell>(this.content, "Logs", out GameObject scrollObj,
|
||||||
|
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
|
||||||
|
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
// Buttons and toggles
|
||||||
|
|
||||||
|
var optionsRow = UIFactory.CreateUIObject("OptionsRow", this.content);
|
||||||
|
UIFactory.SetLayoutElement(optionsRow, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(optionsRow, false, false, true, true, 5, 2, 2, 2, 2);
|
||||||
|
|
||||||
|
var clearButton = UIFactory.CreateButton(optionsRow, "ClearButton", "Clear", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(clearButton.Component.gameObject, minHeight: 23, flexibleHeight: 0, minWidth: 60);
|
||||||
|
clearButton.OnClick += ClearLogs;
|
||||||
|
clearButton.Component.transform.SetSiblingIndex(1);
|
||||||
|
|
||||||
|
var fileButton = UIFactory.CreateButton(optionsRow, "FileButton", "Open Log File", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(fileButton.Component.gameObject, minHeight: 23, flexibleHeight: 0, minWidth: 100);
|
||||||
|
fileButton.OnClick += OpenLogFile;
|
||||||
|
fileButton.Component.transform.SetSiblingIndex(2);
|
||||||
|
|
||||||
|
var unityToggle = UIFactory.CreateToggle(optionsRow, "UnityLogToggle", out var toggle, out var toggleText);
|
||||||
|
UIFactory.SetLayoutElement(unityToggle, minHeight: 25, minWidth: 150);
|
||||||
|
toggleText.text = "Log Unity Debug?";
|
||||||
|
toggle.isOn = ConfigManager.Log_Unity_Debug.Value;
|
||||||
|
ConfigManager.Log_Unity_Debug.OnValueChanged += (bool val) => toggle.isOn = val;
|
||||||
|
toggle.onValueChanged.AddListener((bool val) => ConfigManager.Log_Unity_Debug.Value = val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Log Cell View
|
||||||
|
|
||||||
|
public class ConsoleLogCell : ICell
|
||||||
|
{
|
||||||
|
public Text IndexLabel;
|
||||||
|
public InputFieldRef Input;
|
||||||
|
|
||||||
|
public RectTransform Rect { get; set; }
|
||||||
|
public GameObject UIRoot { get; set; }
|
||||||
|
|
||||||
|
public float DefaultHeight => 25;
|
||||||
|
|
||||||
|
public bool Enabled => UIRoot.activeInHierarchy;
|
||||||
|
public void Enable() => UIRoot.SetActive(true);
|
||||||
|
public void Disable() => UIRoot.SetActive(false);
|
||||||
|
|
||||||
|
|
||||||
|
public GameObject CreateContent(GameObject parent)
|
||||||
|
{
|
||||||
|
UIRoot = UIFactory.CreateUIObject("LogCell", parent, new Vector2(25, 25));
|
||||||
|
Rect = UIRoot.GetComponent<RectTransform>();
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 3);
|
||||||
|
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, minWidth: 50, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
IndexLabel = UIFactory.CreateLabel(UIRoot, "IndexLabel", "i:", TextAnchor.MiddleCenter, Color.grey, false, 12);
|
||||||
|
UIFactory.SetLayoutElement(IndexLabel.gameObject, minHeight: 25, minWidth: 30, flexibleWidth: 40);
|
||||||
|
|
||||||
|
Input = UIFactory.CreateInputField(UIRoot, "Input", "");
|
||||||
|
//Input.Component.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||||||
|
UIFactory.SetLayoutElement(Input.UIRoot, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
RuntimeProvider.Instance.SetColorBlock(Input.Component, new Color(0.1f, 0.1f, 0.1f), new Color(0.13f, 0.13f, 0.13f),
|
||||||
|
new Color(0.07f, 0.07f, 0.07f));
|
||||||
|
Input.Component.GetComponent<Image>().color = new Color(0.2f, 0.2f, 0.2f);
|
||||||
|
|
||||||
|
Input.Component.readOnly = true;
|
||||||
|
Input.Component.textComponent.supportRichText = true;
|
||||||
|
Input.Component.lineType = InputField.LineType.MultiLineNewline;
|
||||||
|
Input.Component.textComponent.font = UIManager.ConsoleFont;
|
||||||
|
Input.PlaceholderText.font = UIManager.ConsoleFont;
|
||||||
|
|
||||||
|
return UIRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
@ -80,7 +80,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
// Save button
|
// Save button
|
||||||
|
|
||||||
var saveBtn = UIFactory.CreateButton(this.content, "Save", "Save Options", new Color(0.25f, 0.3f, 0.25f));
|
var saveBtn = UIFactory.CreateButton(this.content, "Save", "Save Options", new Color(0.2f, 0.3f, 0.2f));
|
||||||
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
|
||||||
saveBtn.OnClick += ConfigManager.Handler.SaveConfig;
|
saveBtn.OnClick += ConfigManager.Handler.SaveConfig;
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ namespace UnityExplorer.UI
|
|||||||
UIPanels.Add(Panels.Inspector, new InspectorPanel());
|
UIPanels.Add(Panels.Inspector, new InspectorPanel());
|
||||||
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
|
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
|
||||||
UIPanels.Add(Panels.Options, new OptionsPanel());
|
UIPanels.Add(Panels.Options, new OptionsPanel());
|
||||||
UIPanels.Add(Panels.ConsoleLog, new ConsoleLogPanel());
|
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
|
||||||
|
|
||||||
foreach (var panel in UIPanels.Values)
|
foreach (var panel in UIPanels.Values)
|
||||||
panel.ConstructUI();
|
panel.ConstructUI();
|
||||||
@ -174,7 +174,6 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||||
|
|
||||||
ExplorerCore.Log("UI initialized.");
|
|
||||||
Initializing = false;
|
Initializing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,8 +278,6 @@ namespace UnityExplorer.UI
|
|||||||
BackupShader = Graphic.defaultGraphicMaterial.shader;
|
BackupShader = Graphic.defaultGraphicMaterial.shader;
|
||||||
|
|
||||||
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
|
ConsoleFont = bundle.LoadAsset<Font>("CONSOLA");
|
||||||
|
|
||||||
ExplorerCore.Log("Loaded UI AssetBundle");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AssetBundle LoadBundle(string id)
|
private static AssetBundle LoadBundle(string id)
|
||||||
|
@ -264,7 +264,7 @@
|
|||||||
<Compile Include="UI\Models\InputFieldRef.cs" />
|
<Compile Include="UI\Models\InputFieldRef.cs" />
|
||||||
<Compile Include="UI\ObjectPool\IPooledObject.cs" />
|
<Compile Include="UI\ObjectPool\IPooledObject.cs" />
|
||||||
<Compile Include="UI\ObjectPool\Pool.cs" />
|
<Compile Include="UI\ObjectPool\Pool.cs" />
|
||||||
<Compile Include="UI\Panels\ConsoleLogPanel.cs" />
|
<Compile Include="UI\Panels\LogPanel.cs" />
|
||||||
<Compile Include="UI\Panels\CSConsolePanel.cs" />
|
<Compile Include="UI\Panels\CSConsolePanel.cs" />
|
||||||
<Compile Include="Core\Utility\IOUtility.cs" />
|
<Compile Include="Core\Utility\IOUtility.cs" />
|
||||||
<Compile Include="UI\Panels\OptionsPanel.cs" />
|
<Compile Include="UI\Panels\OptionsPanel.cs" />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user