REPL console

This commit is contained in:
sinaioutlander 2020-08-07 23:45:09 +10:00
parent df3b41657e
commit 28ba5af6fa
17 changed files with 424 additions and 412 deletions

Binary file not shown.

BIN
Release/CppExplorer.zip Normal file

Binary file not shown.

BIN
Release/mcs.dll Normal file

Binary file not shown.

View File

@ -65,32 +65,31 @@ namespace Explorer
Instance = this;
LoadMCS();
new MainMenu();
new WindowManager();
//LoadMCS();
//// init debugging hooks
//var harmony = HarmonyInstance.Create(ID);
//harmony.PatchAll();
var harmony = HarmonyInstance.Create(ID);
harmony.PatchAll();
// done init
ShowMenu = true;
}
//private void LoadMCS()
//{
// var mcsPath = @"Mods\mcs.dll";
// if (File.Exists(mcsPath))
// {
// Assembly.Load(File.ReadAllBytes(mcsPath));
// MelonLogger.Log("Loaded mcs.dll");
// }
// else
// {
// MelonLogger.LogError("Could not find mcs.dll!");
// }
//}
private void LoadMCS()
{
var mcsPath = @"Mods\mcs.dll";
if (File.Exists(mcsPath))
{
Assembly.Load(File.ReadAllBytes(mcsPath));
MelonLogger.Log("Loaded mcs.dll");
}
else
{
MelonLogger.LogError("Could not find mcs.dll!");
}
}
public override void OnLevelWasLoaded(int level)
{

View File

@ -44,6 +44,11 @@
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\Managed\Il2CppSystem.Core.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="mcs, Version=1.0.0.0, Culture=neutral, processorArchitecture=AMD64">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\Release\mcs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="MelonLoader.ModHandler">
<HintPath>..\..\..\..\..\Steam\steamapps\common\Hellpoint\MelonLoader\MelonLoader.ModHandler.dll</HintPath>
<Private>False</Private>
@ -114,16 +119,16 @@
<ItemGroup>
<Compile Include="ILBehaviour.cs" />
<Compile Include="CppExplorer.cs" />
<Compile Include="Menu\MainMenu\ConsolePage.cs" />
<Compile Include="Menu\MainMenu\Console\REPL.cs" />
<Compile Include="Menu\MainMenu\Console\REPLHelper.cs" />
<Compile Include="MainMenu\Pages\ConsolePage.cs" />
<Compile Include="MainMenu\Pages\Console\REPL.cs" />
<Compile Include="MainMenu\Pages\Console\REPLHelper.cs" />
<Compile Include="WindowManager.cs" />
<Compile Include="Menu\MainMenu.cs" />
<Compile Include="Menu\Inspectors\GameObjectWindow.cs" />
<Compile Include="Menu\Inspectors\ReflectionWindow.cs" />
<Compile Include="Menu\MainMenu\ScenePage.cs" />
<Compile Include="Menu\MainMenu\SearchPage.cs" />
<Compile Include="Menu\UIStyles.cs" />
<Compile Include="MainMenu\MainMenu.cs" />
<Compile Include="Inspectors\GameObjectWindow.cs" />
<Compile Include="Inspectors\ReflectionWindow.cs" />
<Compile Include="MainMenu\Pages\ScenePage.cs" />
<Compile Include="MainMenu\Pages\SearchPage.cs" />
<Compile Include="UIStyles.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="utils\AccessTools.cs" />
</ItemGroup>

View File

@ -19,7 +19,7 @@ namespace Explorer
Pages.Add(new ScenePage());
Pages.Add(new SearchPage());
//Pages.Add(new ConsolePage());
Pages.Add(new ConsolePage());
foreach (var page in Pages)
{

View File

@ -0,0 +1,131 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Mono.CSharp;
using UnityEngine;
using Attribute = System.Attribute;
using Object = UnityEngine.Object;
namespace Explorer
{
public class REPL : InteractiveBase
{
static REPL()
{
var go = new GameObject("UnityREPL");
GameObject.DontDestroyOnLoad(go);
//go.transform.parent = HPExplorer.Instance.transform;
MB = go.AddComponent<ReplHelper>();
}
[Documentation("MB - A dummy MonoBehaviour for accessing Unity.")]
public static ReplHelper MB { get; }
[Documentation("find<T>() - find a UnityEngine.Object of type T.")]
public static T find<T>() where T : Object
{
return MB.Find<T>();
}
[Documentation("findAll<T>() - find all UnityEngine.Object of type T.")]
public static T[] findAll<T>() where T : Object
{
return MB.FindAll<T>();
}
[Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
public static object runCoroutine(IEnumerator i)
{
return MB.RunCoroutine(i);
}
[Documentation("endCoroutine(co) - ends a Unity coroutine.")]
public static void endCoroutine(Coroutine c)
{
MB.EndCoroutine(c);
}
////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
////public static TypeHelper type<T>()
////{
//// return new TypeHelper(typeof(T));
////}
////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")]
////public static TypeHelper type(object instance)
////{
//// return new TypeHelper(instance);
////}
//[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")]
//public static string dir(object instance)
//{
// return type(instance).info();
//}
//[Documentation("dir<T>() - lists all available methods and fields of type T.")]
//public static string dir<T>()
//{
// return type<T>().info();
//}
//[Documentation("findrefs(obj) - find references to the object in currently loaded components.")]
//public static Component[] findrefs(object obj)
//{
// if (obj == null) throw new ArgumentNullException(nameof(obj));
// var results = new List<Component>();
// foreach (var component in Object.FindObjectsOfType<Component>())
// {
// var type = component.GetType();
// var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" };
// var typeBlacklist = new[] { typeof(bool) };
// foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType)))
// {
// try
// {
// if (Equals(prop.GetValue(component, null), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType)))
// {
// try
// {
// if (Equals(field.GetValue(component), obj))
// {
// results.Add(component);
// goto finish;
// }
// }
// catch { }
// }
// finish:;
// }
// return results.ToArray();
//}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
private class DocumentationAttribute : Attribute
{
public DocumentationAttribute(string doc)
{
Docs = doc;
}
public string Docs { get; }
}
}
}

View File

@ -0,0 +1,34 @@
using System.Collections;
//using Il2CppSystem;
using MelonLoader;
using UnityEngine;
using System;
using Object = UnityEngine.Object;
namespace Explorer
{
public class ReplHelper : MonoBehaviour
{
public ReplHelper(IntPtr intPtr) : base(intPtr) { }
public T Find<T>() where T : Object
{
return FindObjectOfType<T>();
}
public T[] FindAll<T>() where T : Object
{
return FindObjectsOfType<T>();
}
public object RunCoroutine(IEnumerator enumerator)
{
return MelonCoroutines.Start(enumerator);
}
public void EndCoroutine(Coroutine c)
{
StopCoroutine(c);
}
}
}

View File

@ -0,0 +1,227 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using System.Reflection;
using Mono.CSharp;
using System.IO;
using MelonLoader;
namespace Explorer
{
public class ConsolePage : MainMenu.WindowPage
{
public override string Name { get => "Console"; set => base.Name = value; }
private ScriptEvaluator _evaluator;
private readonly StringBuilder _sb = new StringBuilder();
private string MethodInput = "";
private string UsingInput = "";
public static List<string> UsingDirectives;
private static readonly string[] m_defaultUsing = new string[]
{
"System",
"UnityEngine",
"System.Linq",
"System.Collections",
"System.Collections.Generic",
"System.Reflection",
"MelonLoader"
};
public override void Init()
{
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<ReplHelper>();
try
{
MethodInput = @"// This is a basic REPL console used to execute a method.
// Some common directives are added by default, you can add more below.
// If you want to return some output, MelonLogger.Log() it.
MelonLogger.Log(""hello world"");";
ResetConsole();
}
catch (Exception e)
{
MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}");
}
}
public void ResetConsole()
{
if (_evaluator != null)
{
_evaluator.Dispose();
}
_evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) };
UsingDirectives = new List<string>();
UsingDirectives.AddRange(m_defaultUsing);
foreach (string asm in UsingDirectives)
{
Evaluate(AsmToUsing(asm));
}
}
public string AsmToUsing(string asm, bool richtext = false)
{
if (richtext)
{
return $"<color=#569cd6>using</color> {asm};";
}
return $"using {asm};";
}
public void AddUsing(string asm)
{
if (!UsingDirectives.Contains(asm))
{
UsingDirectives.Add(asm);
Evaluate(AsmToUsing(asm));
}
}
public object Evaluate(string str)
{
object ret = VoidType.Value;
_evaluator.Compile(str, out var compiled);
try
{
compiled?.Invoke(ref ret);
}
catch (Exception e)
{
MelonLogger.LogWarning(e.ToString());
}
return ret;
}
public override void DrawWindow()
{
GUILayout.Label("<b><size=15><color=cyan>REPL Console</color></size></b>", null);
GUILayout.Label("Method:", null);
MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) });
if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
{
try
{
MethodInput = MethodInput.Trim();
if (!string.IsNullOrEmpty(MethodInput))
{
var result = Evaluate(MethodInput);
if (result != null && !Equals(result, VoidType.Value))
{
MelonLogger.Log("[Console Output]\r\n" + result.ToString());
}
}
}
catch (Exception e)
{
MelonLogger.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
}
}
GUILayout.Label("<b>Using directives:</b>", null);
foreach (var asm in UsingDirectives)
{
GUILayout.Label(AsmToUsing(asm, true), null);
}
GUILayout.BeginHorizontal(null);
GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(50) }))
{
AddUsing(UsingInput);
}
if (GUILayout.Button("<color=red>Reset</color>", null))
{
ResetConsole();
}
GUILayout.EndHorizontal();
}
public override void Update() { }
private class VoidType
{
public static readonly VoidType Value = new VoidType();
private VoidType() { }
}
}
internal class ScriptEvaluator : Evaluator, IDisposable
{
private static readonly HashSet<string> StdLib =
new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" };
private readonly TextWriter _logger;
public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
{
_logger = logger;
ImportAppdomainAssemblies(ReferenceAssembly);
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
}
public void Dispose()
{
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
_logger.Dispose();
}
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
string name = args.LoadedAssembly.GetName().Name;
if (StdLib.Contains(name))
return;
ReferenceAssembly(args.LoadedAssembly);
}
private static CompilerContext BuildContext(TextWriter tw)
{
var reporter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
{
Version = LanguageVersion.Experimental,
GenerateDebugInfo = false,
StdLib = true,
Target = Target.Library,
WarningLevel = 0,
EnhancedWarnings = false
};
return new CompilerContext(settings, reporter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)
{
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
{
string name = assembly.GetName().Name;
if (StdLib.Contains(name))
continue;
import(assembly);
}
}
}
}

View File

@ -1,131 +0,0 @@
//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Linq;
//using System.Reflection;
//using System.Text;
//using Mono.CSharp;
//using UnityEngine;
//using Attribute = System.Attribute;
//using Object = UnityEngine.Object;
//namespace Explorer
//{
// public class REPL : InteractiveBase
// {
// static REPL()
// {
// var go = new GameObject("UnityREPL");
// GameObject.DontDestroyOnLoad(go);
// //go.transform.parent = HPExplorer.Instance.transform;
// MB = go.AddComponent<ReplHelper>();
// }
// [Documentation("MB - A dummy MonoBehaviour for accessing Unity.")]
// public static ReplHelper MB { get; }
// [Documentation("find<T>() - find a UnityEngine.Object of type T.")]
// public static T find<T>() where T : Object
// {
// return MB.Find<T>();
// }
// [Documentation("findAll<T>() - find all UnityEngine.Object of type T.")]
// public static T[] findAll<T>() where T : Object
// {
// return MB.FindAll<T>();
// }
// [Documentation("runCoroutine(enumerator) - runs an IEnumerator as a Unity coroutine.")]
// public static object runCoroutine(IEnumerator i)
// {
// return MB.RunCoroutine(i);
// }
// [Documentation("endCoroutine(co) - ends a Unity coroutine.")]
// public static void endCoroutine(Coroutine c)
// {
// MB.EndCoroutine(c);
// }
// ////[Documentation("type<T>() - obtain type info about a type T. Provides some Reflection helpers.")]
// ////public static TypeHelper type<T>()
// ////{
// //// return new TypeHelper(typeof(T));
// ////}
// ////[Documentation("type(obj) - obtain type info about object obj. Provides some Reflection helpers.")]
// ////public static TypeHelper type(object instance)
// ////{
// //// return new TypeHelper(instance);
// ////}
// //[Documentation("dir(obj) - lists all available methods and fiels of a given obj.")]
// //public static string dir(object instance)
// //{
// // return type(instance).info();
// //}
// //[Documentation("dir<T>() - lists all available methods and fields of type T.")]
// //public static string dir<T>()
// //{
// // return type<T>().info();
// //}
// //[Documentation("findrefs(obj) - find references to the object in currently loaded components.")]
// //public static Component[] findrefs(object obj)
// //{
// // if (obj == null) throw new ArgumentNullException(nameof(obj));
// // var results = new List<Component>();
// // foreach (var component in Object.FindObjectsOfType<Component>())
// // {
// // var type = component.GetType();
// // var nameBlacklist = new[] { "parent", "parentInternal", "root", "transform", "gameObject" };
// // var typeBlacklist = new[] { typeof(bool) };
// // foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// // .Where(x => x.CanRead && !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.PropertyType)))
// // {
// // try
// // {
// // if (Equals(prop.GetValue(component, null), obj))
// // {
// // results.Add(component);
// // goto finish;
// // }
// // }
// // catch { }
// // }
// // foreach (var field in type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
// // .Where(x => !nameBlacklist.Contains(x.Name) && !typeBlacklist.Contains(x.FieldType)))
// // {
// // try
// // {
// // if (Equals(field.GetValue(component), obj))
// // {
// // results.Add(component);
// // goto finish;
// // }
// // }
// // catch { }
// // }
// // finish:;
// // }
// // return results.ToArray();
// //}
// [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
// private class DocumentationAttribute : Attribute
// {
// public DocumentationAttribute(string doc)
// {
// Docs = doc;
// }
// public string Docs { get; }
// }
// }
//}

View File

@ -1,29 +0,0 @@
//using System.Collections;
//using MelonLoader;
//using UnityEngine;
//namespace Explorer
//{
// public class ReplHelper : MonoBehaviour
// {
// public T Find<T>() where T : Object
// {
// return FindObjectOfType<T>();
// }
// public T[] FindAll<T>() where T : Object
// {
// return FindObjectsOfType<T>();
// }
// public object RunCoroutine(IEnumerator enumerator)
// {
// return MelonCoroutines.Start(enumerator);
// }
// public void EndCoroutine(Coroutine c)
// {
// StopCoroutine(c);
// }
// }
//}

View File

@ -1,224 +0,0 @@
//using System;
//using System.Collections;
//using System.Collections.Generic;
//using System.Linq;
//using System.Text;
//using System.Threading.Tasks;
//using UnityEngine;
//using System.Reflection;
//using Mono.CSharp;
//using System.IO;
//using MelonLoader;
//namespace Explorer
//{
// public class ConsolePage : MainMenu.WindowPage
// {
// public override string Name { get => "Console"; set => base.Name = value; }
// private ScriptEvaluator _evaluator;
// private readonly StringBuilder _sb = new StringBuilder();
// private string MethodInput = "";
// private string UsingInput = "";
// public static List<string> UsingDirectives;
// private static readonly string[] m_defaultUsing = new string[]
// {
// "System",
// "UnityEngine",
// "System.Linq",
// "System.Collections",
// "System.Collections.Generic",
// "System.Reflection"
// };
// public override void Init()
// {
// try
// {
// MethodInput = @"// This is a basic REPL console used to execute a method.
//// Some common directives are added by default, you can add more below.
//// If you want to return some output, Debug.Log() it.
//Debug.Log(""hello world"");";
// ResetConsole();
// }
// catch (Exception e)
// {
// MelonLogger.Log($"Error setting up console!\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}");
// }
// }
// public void ResetConsole()
// {
// if (_evaluator != null)
// {
// _evaluator.Dispose();
// }
// _evaluator = new ScriptEvaluator(new StringWriter(_sb)) { InteractiveBaseClass = typeof(REPL) };
// UsingDirectives = new List<string>();
// UsingDirectives.AddRange(m_defaultUsing);
// foreach (string asm in UsingDirectives)
// {
// Evaluate(AsmToUsing(asm));
// }
// }
// public string AsmToUsing(string asm, bool richtext = false)
// {
// if (richtext)
// {
// return $"<color=#569cd6>using</color> {asm};";
// }
// return $"using {asm};";
// }
// public void AddUsing(string asm)
// {
// if (!UsingDirectives.Contains(asm))
// {
// UsingDirectives.Add(asm);
// Evaluate(AsmToUsing(asm));
// }
// }
// public object Evaluate(string str)
// {
// object ret = VoidType.Value;
// _evaluator.Compile(str, out var compiled);
// try
// {
// compiled?.Invoke(ref ret);
// }
// catch (Exception e)
// {
// MelonLogger.LogWarning(e.ToString());
// }
// return ret;
// }
// public override void DrawWindow()
// {
// GUILayout.Label("<b><size=15><color=cyan>REPL Console</color></size></b>", null);
// GUILayout.Label("Method:", null);
// MethodInput = GUILayout.TextArea(MethodInput, new GUILayoutOption[] { GUILayout.Height(300) });
// if (GUILayout.Button("<color=cyan><b>Execute</b></color>", null))
// {
// try
// {
// MethodInput = MethodInput.Trim();
// if (!string.IsNullOrEmpty(MethodInput))
// {
// var result = Evaluate(MethodInput);
// if (result != null && !Equals(result, VoidType.Value))
// {
// MelonLogger.Log("[Console Output]\r\n" + result.ToString());
// }
// }
// }
// catch (Exception e)
// {
// MelonLogger.LogError("Exception compiling!\r\nMessage: " + e.Message + "\r\nStack: " + e.StackTrace);
// }
// }
// GUILayout.Label("<b>Using directives:</b>", null);
// foreach (var asm in UsingDirectives)
// {
// GUILayout.Label(AsmToUsing(asm, true), null);
// }
// GUILayout.BeginHorizontal(null);
// GUILayout.Label("Add namespace:", new GUILayoutOption[] { GUILayout.Width(110) });
// UsingInput = GUILayout.TextField(UsingInput, new GUILayoutOption[] { GUILayout.Width(150) });
// if (GUILayout.Button("Add", new GUILayoutOption[] { GUILayout.Width(50) }))
// {
// AddUsing(UsingInput);
// }
// if (GUILayout.Button("<color=red>Reset</color>", null))
// {
// ResetConsole();
// }
// GUILayout.EndHorizontal();
// }
// public override void Update() { }
// private class VoidType
// {
// public static readonly VoidType Value = new VoidType();
// private VoidType() { }
// }
// }
// internal class ScriptEvaluator : Evaluator, IDisposable
// {
// private static readonly HashSet<string> StdLib =
// new HashSet<string>(StringComparer.InvariantCultureIgnoreCase) { "mscorlib", "System.Core", "System", "System.Xml" };
// private readonly TextWriter _logger;
// public ScriptEvaluator(TextWriter logger) : base(BuildContext(logger))
// {
// _logger = logger;
// ImportAppdomainAssemblies(ReferenceAssembly);
// AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
// }
// public void Dispose()
// {
// AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
// _logger.Dispose();
// }
// private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
// {
// string name = args.LoadedAssembly.GetName().Name;
// if (StdLib.Contains(name))
// return;
// ReferenceAssembly(args.LoadedAssembly);
// }
// private static CompilerContext BuildContext(TextWriter tw)
// {
// var reporter = new StreamReportPrinter(tw);
// var settings = new CompilerSettings
// {
// Version = LanguageVersion.Experimental,
// GenerateDebugInfo = false,
// StdLib = true,
// Target = Target.Library,
// WarningLevel = 0,
// EnhancedWarnings = false
// };
// return new CompilerContext(settings, reporter);
// }
// private static void ImportAppdomainAssemblies(Action<Assembly> import)
// {
// foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
// {
// string name = assembly.GetName().Name;
// if (StdLib.Contains(name))
// continue;
// import(assembly);
// }
// }
// }
//}