Skip to content
This repository was archived by the owner on May 9, 2023. It is now read-only.

Commit 2107df7

Browse files
committed
Improvements to CS Console
* Errors are now logged properly. * Can now define classes, methods, etc - no longer has to be an expression body. * Added `StartCoroutine(IEnumerator routine)` helper method to easily run a Coroutine * Disabling suggestions now properly stops Explorer trying to update suggestion cache instead of just not showing them. In the rare cases that suggestions cause a crash, disabling them will now prevent those crashes. * Various other misc improvements behind the scenes
1 parent a9fbea7 commit 2107df7

File tree

11 files changed

+289
-56
lines changed

11 files changed

+289
-56
lines changed

src/Core/CSharp/DummyBehaviour.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#if MONO
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using UnityEngine;
7+
8+
namespace UnityExplorer.Core.CSharp
9+
{
10+
public class DummyBehaviour : MonoBehaviour
11+
{
12+
public static DummyBehaviour Instance;
13+
14+
public static void Setup()
15+
{
16+
var obj = new GameObject("Explorer_DummyBehaviour");
17+
DontDestroyOnLoad(obj);
18+
obj.hideFlags |= HideFlags.HideAndDontSave;
19+
20+
obj.AddComponent<DummyBehaviour>();
21+
}
22+
23+
internal void Awake()
24+
{
25+
Instance = this;
26+
}
27+
}
28+
}
29+
#endif

src/Core/CSharp/ScriptEvaluator.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ public class ScriptEvaluator : Evaluator, IDisposable
1515
"mscorlib", "System.Core", "System", "System.Xml"
1616
};
1717

18-
private readonly TextWriter tw;
18+
internal static TextWriter _textWriter;
19+
internal static StreamReportPrinter _reportPrinter;
1920

2021
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
2122
{
22-
this.tw = tw;
23+
_textWriter = tw;
2324

2425
ImportAppdomainAssemblies(ReferenceAssembly);
2526
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
@@ -28,23 +29,22 @@ public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
2829
public void Dispose()
2930
{
3031
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
31-
tw.Dispose();
32+
_textWriter.Dispose();
3233
}
3334

3435
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
3536
{
3637
string name = args.LoadedAssembly.GetName().Name;
38+
3739
if (StdLib.Contains(name))
38-
{
3940
return;
40-
}
4141

4242
ReferenceAssembly(args.LoadedAssembly);
4343
}
4444

4545
private static CompilerContext BuildContext(TextWriter tw)
4646
{
47-
var reporter = new StreamReportPrinter(tw);
47+
_reportPrinter = new StreamReportPrinter(tw);
4848

4949
var settings = new CompilerSettings
5050
{
@@ -56,7 +56,7 @@ private static CompilerContext BuildContext(TextWriter tw)
5656
EnhancedWarnings = false
5757
};
5858

59-
return new CompilerContext(settings, reporter);
59+
return new CompilerContext(settings, _reportPrinter);
6060
}
6161

6262
private static void ImportAppdomainAssemblies(Action<Assembly> import)

src/Core/CSharp/ScriptInteraction.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44
using UnityExplorer.UI.Main;
55
using UnityExplorer.Core.Inspectors;
66
using UnityExplorer.UI.Main.CSConsole;
7+
using System.Collections;
8+
using UnityEngine;
9+
using System.Collections.Generic;
10+
using System.Linq;
11+
using UnityExplorer.Core.Runtime;
712

813
namespace UnityExplorer.Core.CSharp
914
{
@@ -14,14 +19,19 @@ public static void Log(object message)
1419
ExplorerCore.Log(message);
1520
}
1621

22+
public static void StartCoroutine(IEnumerator ienumerator)
23+
{
24+
RuntimeProvider.Instance.StartConsoleCoroutine(ienumerator);
25+
}
26+
1727
public static void AddUsing(string directive)
1828
{
1929
CSharpConsole.Instance.AddUsing(directive);
2030
}
2131

2232
public static void GetUsing()
2333
{
24-
ExplorerCore.Log(CSharpConsole.Instance.m_evaluator.GetUsing());
34+
ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
2535
}
2636

2737
public static void Reset()
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
#if CPP
2+
using System;
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using UnhollowerBaseLib;
7+
using UnityEngine;
8+
9+
// CREDIT HerpDerpenstine
10+
// https://github.com/LavaGang/MelonLoader/blob/master/MelonLoader.Support.Il2Cpp/MelonCoroutines.cs
11+
12+
namespace UnityExplorer.Core.Runtime.Il2Cpp
13+
{
14+
public static class Il2CppCoroutine
15+
{
16+
private struct CoroTuple
17+
{
18+
public object WaitCondition;
19+
public IEnumerator Coroutine;
20+
}
21+
private static readonly List<CoroTuple> ourCoroutinesStore = new List<CoroTuple>();
22+
private static readonly List<IEnumerator> ourNextFrameCoroutines = new List<IEnumerator>();
23+
private static readonly List<IEnumerator> ourWaitForFixedUpdateCoroutines = new List<IEnumerator>();
24+
private static readonly List<IEnumerator> ourWaitForEndOfFrameCoroutines = new List<IEnumerator>();
25+
26+
private static readonly List<IEnumerator> tempList = new List<IEnumerator>();
27+
28+
internal static object Start(IEnumerator routine)
29+
{
30+
if (routine != null) ProcessNextOfCoroutine(routine);
31+
return routine;
32+
}
33+
34+
internal static void Stop(IEnumerator enumerator)
35+
{
36+
if (ourNextFrameCoroutines.Contains(enumerator)) // the coroutine is running itself
37+
ourNextFrameCoroutines.Remove(enumerator);
38+
else
39+
{
40+
int coroTupleIndex = ourCoroutinesStore.FindIndex(c => c.Coroutine == enumerator);
41+
if (coroTupleIndex != -1) // the coroutine is waiting for a subroutine
42+
{
43+
object waitCondition = ourCoroutinesStore[coroTupleIndex].WaitCondition;
44+
if (waitCondition is IEnumerator waitEnumerator)
45+
Stop(waitEnumerator);
46+
47+
ourCoroutinesStore.RemoveAt(coroTupleIndex);
48+
}
49+
}
50+
}
51+
52+
private static void ProcessCoroList(List<IEnumerator> target)
53+
{
54+
if (target.Count == 0) return;
55+
56+
// use a temp list to make sure waits made during processing are not handled by same processing invocation
57+
// additionally, a temp list reduces allocations compared to an array
58+
tempList.AddRange(target);
59+
target.Clear();
60+
foreach (var enumerator in tempList) ProcessNextOfCoroutine(enumerator);
61+
tempList.Clear();
62+
}
63+
64+
internal static void Process()
65+
{
66+
for (var i = ourCoroutinesStore.Count - 1; i >= 0; i--)
67+
{
68+
var tuple = ourCoroutinesStore[i];
69+
if (tuple.WaitCondition is WaitForSeconds waitForSeconds)
70+
{
71+
if ((waitForSeconds.m_Seconds -= Time.deltaTime) <= 0)
72+
{
73+
ourCoroutinesStore.RemoveAt(i);
74+
ProcessNextOfCoroutine(tuple.Coroutine);
75+
}
76+
}
77+
}
78+
79+
ProcessCoroList(ourNextFrameCoroutines);
80+
}
81+
82+
internal static void ProcessWaitForFixedUpdate() => ProcessCoroList(ourWaitForFixedUpdateCoroutines);
83+
84+
internal static void ProcessWaitForEndOfFrame() => ProcessCoroList(ourWaitForEndOfFrameCoroutines);
85+
86+
private static void ProcessNextOfCoroutine(IEnumerator enumerator)
87+
{
88+
try
89+
{
90+
if (!enumerator.MoveNext()) // Run the next step of the coroutine. If it's done, restore the parent routine
91+
{
92+
var indices = ourCoroutinesStore.Select((it, idx) => (idx, it)).Where(it => it.it.WaitCondition == enumerator).Select(it => it.idx).ToList();
93+
for (var i = indices.Count - 1; i >= 0; i--)
94+
{
95+
var index = indices[i];
96+
ourNextFrameCoroutines.Add(ourCoroutinesStore[index].Coroutine);
97+
ourCoroutinesStore.RemoveAt(index);
98+
}
99+
return;
100+
}
101+
}
102+
catch (Exception e)
103+
{
104+
ExplorerCore.LogError(e.ToString());
105+
Stop(FindOriginalCoro(enumerator)); // We want the entire coroutine hierachy to stop when an error happen
106+
}
107+
108+
var next = enumerator.Current;
109+
switch (next)
110+
{
111+
case null:
112+
ourNextFrameCoroutines.Add(enumerator);
113+
return;
114+
case WaitForFixedUpdate _:
115+
ourWaitForFixedUpdateCoroutines.Add(enumerator);
116+
return;
117+
case WaitForEndOfFrame _:
118+
ourWaitForEndOfFrameCoroutines.Add(enumerator);
119+
return;
120+
case WaitForSeconds _:
121+
break; // do nothing, this one is supported in Process
122+
case Il2CppObjectBase il2CppObjectBase:
123+
var nextAsEnumerator = il2CppObjectBase.TryCast<Il2CppSystem.Collections.IEnumerator>();
124+
if (nextAsEnumerator != null) // il2cpp IEnumerator also handles CustomYieldInstruction
125+
next = new Il2CppEnumeratorWrapper(nextAsEnumerator);
126+
else
127+
ExplorerCore.LogWarning($"Unknown coroutine yield object of type {il2CppObjectBase} for coroutine {enumerator}");
128+
break;
129+
}
130+
131+
ourCoroutinesStore.Add(new CoroTuple { WaitCondition = next, Coroutine = enumerator });
132+
133+
if (next is IEnumerator nextCoro)
134+
ProcessNextOfCoroutine(nextCoro);
135+
}
136+
137+
private static IEnumerator FindOriginalCoro(IEnumerator enumerator)
138+
{
139+
int index = ourCoroutinesStore.FindIndex(ct => ct.WaitCondition == enumerator);
140+
if (index == -1)
141+
return enumerator;
142+
return FindOriginalCoro(ourCoroutinesStore[index].Coroutine);
143+
}
144+
145+
private class Il2CppEnumeratorWrapper : IEnumerator
146+
{
147+
private readonly Il2CppSystem.Collections.IEnumerator il2cppEnumerator;
148+
149+
public Il2CppEnumeratorWrapper(Il2CppSystem.Collections.IEnumerator il2CppEnumerator) => il2cppEnumerator = il2CppEnumerator;
150+
public bool MoveNext() => il2cppEnumerator.MoveNext();
151+
public void Reset() => il2cppEnumerator.Reset();
152+
public object Current => il2cppEnumerator.Current;
153+
}
154+
}
155+
}
156+
#endif

src/Core/Runtime/Il2Cpp/Il2CppProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using UnityEngine;
1111
using UnityEngine.Events;
1212
using UnityEngine.SceneManagement;
13+
using System.Collections;
1314

1415
namespace UnityExplorer.Core.Runtime.Il2Cpp
1516
{
@@ -41,6 +42,11 @@ public override void SetupEvents()
4142
}
4243
}
4344

45+
public override void StartConsoleCoroutine(IEnumerator routine)
46+
{
47+
Il2CppCoroutine.Start(routine);
48+
}
49+
4450
internal delegate IntPtr d_LayerToName(int layer);
4551

4652
public override string LayerToName(int layer)

src/Core/Runtime/Mono/MonoProvider.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#if MONO
22
using System;
3+
using System.Collections;
34
using System.Collections.Generic;
45
using System.Linq;
56
using System.Reflection;
67
using System.Text;
78
using UnityEngine;
89
using UnityEngine.SceneManagement;
910
using UnityExplorer.Core;
11+
using UnityExplorer.Core.CSharp;
1012

1113
namespace UnityExplorer.Core.Runtime.Mono
1214
{
@@ -25,6 +27,11 @@ public override void SetupEvents()
2527
//SceneManager.activeSceneChanged += ExplorerCore.Instance.OnSceneLoaded2;
2628
}
2729

30+
public override void StartConsoleCoroutine(IEnumerator routine)
31+
{
32+
DummyBehaviour.Instance.StartCoroutine(routine);
33+
}
34+
2835
public override string LayerToName(int layer)
2936
=> LayerMask.LayerToName(layer);
3037

@@ -50,4 +57,12 @@ public override int GetRootCount(Scene scene)
5057
}
5158
}
5259

60+
public static class MonoExtensions
61+
{
62+
public static void Clear(this StringBuilder sb)
63+
{
64+
sb.Remove(0, sb.Length);
65+
}
66+
}
67+
5368
#endif

src/Core/Runtime/RuntimeProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Text;
@@ -36,6 +37,8 @@ public static void Init() =>
3637

3738
public abstract void SetupEvents();
3839

40+
public abstract void StartConsoleCoroutine(IEnumerator routine);
41+
3942
// Unity API handlers
4043

4144
public abstract string LayerToName(int layer);

src/UI/Main/CSConsole/AutoCompleter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ public static void GetAutocompletes(string input)
205205
{
206206
// Credit ManylMarco
207207
CSharpConsole.AutoCompletes.Clear();
208-
string[] completions = CSharpConsole.Instance.m_evaluator.GetCompletions(input, out string prefix);
208+
string[] completions = CSharpConsole.Instance.Evaluator.GetCompletions(input, out string prefix);
209209
if (completions != null)
210210
{
211211
if (prefix == null)

src/UI/Main/CSConsole/CSLexerHighlighter.cs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,10 @@ public class CSLexerHighlighter
5353
"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group",
5454
"if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out",
5555
"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
56-
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield" }
57-
};
58-
59-
public static KeywordMatch invalidKeywordMatcher = new KeywordMatch()
60-
{
61-
highlightColor = new Color(0.95f, 0.10f, 0.10f, 1.0f),
62-
Keywords = new[] { "abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
63-
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
64-
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" }
56+
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield",
57+
"abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
58+
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
59+
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void"}
6560
};
6661

6762
// ~~~~~~~ ctor ~~~~~~~
@@ -78,7 +73,6 @@ public CSLexerHighlighter()
7873
numberMatcher,
7974
stringMatcher,
8075
validKeywordMatcher,
81-
invalidKeywordMatcher,
8276
};
8377

8478
foreach (Matcher lexer in matchers)

0 commit comments

Comments
 (0)