Last active
March 19, 2020 05:57
-
-
Save 20chan/1199b5100c96fc146d6aff8f454bba5a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.CodeDom.Compiler; | |
using System.Collections.Generic; | |
using System.Text; | |
using System.Threading; | |
using Microsoft.CSharp; | |
using UnityEditor; | |
using UnityEngine; | |
using Object = UnityEngine.Object; | |
public class UnitySniper : EditorWindow { | |
public Object[] parameters; | |
public string codeName; | |
public string code; | |
public string output; | |
public string usingCode = "using UnityEngine;\nusing System.Linq;"; | |
public Object inspect; | |
public List<Snippet> snippets; | |
private bool running, runComplete; | |
private bool lastError; | |
private Func<bool> compileCallback; | |
[MenuItem("Sniper/Open")] | |
static void Init() { | |
GetWindow<UnitySniper>(); | |
} | |
private void OnEnable() { | |
var data = EditorPrefs.GetString("SniperSnippets", "{}"); | |
snippets = JsonUtility.FromJson<Snippets>(data).snippets; | |
} | |
private void OnGUI() { | |
if (running && runComplete) { | |
running = false; | |
lastError = compileCallback(); | |
} | |
EditorGUILayout.LabelField("Code Sniper", EditorStyles.boldLabel); | |
EditorGUILayout.BeginHorizontal(); | |
EditorGUILayout.BeginVertical(); | |
var serial = new SerializedObject(this); | |
var parametersProp = serial.FindProperty(nameof(parameters)); | |
EditorGUILayout.PropertyField(parametersProp, true); | |
serial.ApplyModifiedProperties(); | |
EditorGUILayout.LabelField("usings"); | |
usingCode = EditorGUILayout.TextArea(usingCode); | |
codeName = EditorGUILayout.TextField("code", codeName); | |
code = EditorGUILayout.TextArea(code, GUILayout.ExpandHeight(true)); | |
EditorGUILayout.LabelField("output"); | |
EditorGUILayout.TextArea(output); | |
var buttonStyle = new GUIStyle(GUI.skin.button); | |
var runningButtonColor = new GUIStyle(GUI.skin.button); | |
runningButtonColor.normal.textColor = Color.black; | |
var errorButtonColor = new GUIStyle(GUI.skin.button); | |
errorButtonColor.normal.textColor = Color.red; | |
var runStyle = running | |
? runningButtonColor | |
: lastError | |
? errorButtonColor | |
: buttonStyle; | |
GUI.enabled = !running; | |
if (GUILayout.Button("Run", runStyle, GUILayout.Height(40))) { | |
new Thread(() => { | |
running = true; | |
runComplete = false; | |
try { | |
compileCallback = Execute(code); | |
} finally { | |
runComplete = true; | |
} | |
}).Start(); | |
} | |
GUI.enabled = true; | |
EditorGUILayout.EndVertical(); | |
EditorGUILayout.BeginVertical(GUILayout.Width(200)); | |
for (var i = 0; i < snippets.Count; i++) { | |
var s = snippets[i]; | |
EditorGUILayout.BeginHorizontal(); | |
GUI.enabled = !running; | |
if (GUILayout.Button(s.name, GUILayout.ExpandHeight(true))) { | |
new Thread(() => { | |
running = true; | |
runComplete = false; | |
try { | |
compileCallback = Execute(s.code); | |
} finally { | |
runComplete = true; | |
} | |
}).Start(); | |
} | |
GUI.enabled = true; | |
EditorGUILayout.BeginVertical(GUILayout.Width(50)); | |
if (GUILayout.Button("Load")) { | |
codeName = s.name; | |
code = s.code; | |
} | |
if (GUILayout.Button("Code")) { | |
code = s.code; | |
} | |
if (GUILayout.Button("Save")) { | |
s.name = codeName; | |
s.code = code; | |
Save(); | |
} | |
EditorGUILayout.EndVertical(); | |
EditorGUILayout.BeginVertical(GUILayout.Width(10)); | |
if (GUILayout.Button("▲")) { | |
if (i != 0) { | |
snippets.RemoveAt(i); | |
snippets.Insert(i - 1, s); | |
Save(); | |
} | |
} | |
if (GUILayout.Button("x")) { | |
snippets.RemoveAt(i); | |
i--; | |
Save(); | |
} | |
if (GUILayout.Button("▼")) { | |
if (i != snippets.Count - 1) { | |
snippets.RemoveAt(i); | |
snippets.Insert(i + 1, s); | |
Save(); | |
} | |
} | |
EditorGUILayout.EndVertical(); | |
EditorGUILayout.EndHorizontal(); | |
} | |
if (GUILayout.Button("Add")) { | |
snippets.Add(new Snippet { name = codeName, code = code }); | |
Save(); | |
} | |
GUILayout.FlexibleSpace(); | |
EditorGUILayout.BeginHorizontal(); | |
var inspectProp = serial.FindProperty(nameof(inspect)); | |
EditorGUILayout.LabelField("inspect", GUILayout.Width(50)); | |
EditorGUILayout.PropertyField(inspectProp, GUIContent.none, true); | |
EditorGUILayout.EndHorizontal(); | |
EditorGUILayout.Separator(); | |
EditorGUILayout.EndVertical(); | |
EditorGUILayout.EndHorizontal(); | |
} | |
private void Save() { | |
var data = JsonUtility.ToJson(new Snippets { snippets = snippets }); | |
EditorPrefs.SetString("SniperSnippets", data); | |
} | |
private Func<bool> Execute(string code) { | |
const string prefix = "namespace AUTONAMESPACE { public class AUTOCLASS { public UnityEngine.Object inspect; public System.Text.StringBuilder AUTOSB = new System.Text.StringBuilder(); public void AUTOMETHOD(UnityEngine.Object[] parameters) {"; | |
const string postfix = "} public void print(object o) { AUTOSB.AppendLine(o.ToString()); } } }"; | |
var fullCode = string.Concat(usingCode, prefix, code, postfix); | |
var provider = new CSharpCodeProvider(); | |
var options = new CompilerParameters { | |
GenerateInMemory = true, | |
GenerateExecutable = false, | |
}; | |
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { | |
if (assembly.IsDynamic) continue; | |
options.ReferencedAssemblies.Add(assembly.Location); | |
} | |
var result = provider.CompileAssemblyFromSource(options, fullCode); | |
var hasError = false; | |
foreach (CompilerError error in result.Errors) { | |
if (!error.IsWarning) { | |
hasError = true; | |
break; | |
} | |
} | |
if (hasError) { | |
var sb = new StringBuilder(); | |
sb.AppendLine("COMPILE ERROR"); | |
foreach (CompilerError error in result.Errors) { | |
sb.AppendFormat("Error {0}: {1}\n", error.ErrorNumber, error.ErrorText); | |
} | |
output = sb.ToString(); | |
return () => { | |
output = sb.ToString(); | |
return false; | |
}; | |
} | |
var instance = result.CompiledAssembly.CreateInstance("AUTONAMESPACE.AUTOCLASS"); | |
var method = instance.GetType().GetMethod("AUTOMETHOD"); | |
return () => { | |
try { | |
method.Invoke(instance, new object[] { parameters }); | |
} catch (Exception ex) { | |
var sb = new StringBuilder(); | |
sb.AppendLine("RUNTIME ERROR"); | |
sb.Append(ex.ToString()); | |
output = sb.ToString(); | |
return false; | |
} | |
var sbField = instance.GetType().GetField("AUTOSB"); | |
output = sbField.GetValue(instance).ToString(); | |
var inspectField = instance.GetType().GetField("inspect"); | |
inspect = (Object)inspectField.GetValue(instance); | |
return true; | |
}; | |
} | |
[Serializable] | |
public class Snippets { | |
public List<Snippet> snippets; | |
} | |
[Serializable] | |
public class Snippet { | |
public string name; | |
public string code; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment