修改提交
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Codely.Newtonsoft.Json.Linq;
|
||||
using UnityEngine;
|
||||
using UnityTcp.Editor.Helpers;
|
||||
using Microsoft.CodeAnalysis.CSharp.Scripting;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
|
||||
|
||||
namespace UnityTcp.Editor.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Executes C# scripts using Microsoft.CodeAnalysis.CSharp.Scripting (Roslyn).
|
||||
/// Captures and returns logs generated during script execution.
|
||||
/// Ensures execution happens on the main thread.
|
||||
/// </summary>
|
||||
public static class ExecuteCSharpScript
|
||||
{
|
||||
private static List<string> _capturedLogs = new List<string>();
|
||||
private static bool _isCapturingLogs = false;
|
||||
|
||||
/// <summary>
|
||||
/// Main handler for executing C# scripts.
|
||||
/// </summary>
|
||||
public static object HandleCommand(JObject @params)
|
||||
{
|
||||
string script = @params["script"]?.ToString();
|
||||
if (string.IsNullOrEmpty(script))
|
||||
{
|
||||
return Response.Error("'script' parameter is required.");
|
||||
}
|
||||
|
||||
bool captureLogs = @params["capture_logs"]?.ToObject<bool>() ?? true;
|
||||
string[] imports = @params["imports"]?.ToObject<string[]>() ?? new string[]
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
"System.Collections.Generic",
|
||||
"UnityEngine",
|
||||
"UnityEditor",
|
||||
"UnityEditor.SceneManagement",
|
||||
"UnityEngine.SceneManagement"
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
Debug.Log($"[ExecuteCSharpScript] Executing C# script (length: {script.Length} chars)");
|
||||
|
||||
StartLogCapture(captureLogs);
|
||||
|
||||
object result;
|
||||
try
|
||||
{
|
||||
result = ExecuteScriptInternal(script, imports);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// Always stop log capture, even on error
|
||||
}
|
||||
|
||||
var logs = captureLogs ? StopLogCapture() : new List<string>();
|
||||
|
||||
return Response.Success(
|
||||
"C# script executed successfully.",
|
||||
new
|
||||
{
|
||||
result = result?.ToString(),
|
||||
logs = logs,
|
||||
log_count = logs.Count
|
||||
}
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
var logs = captureLogs ? StopLogCapture() : new List<string>();
|
||||
Debug.LogError($"[ExecuteCSharpScript] Failed to execute script: {e}");
|
||||
return Response.Error(
|
||||
$"C# script execution failed: {e.Message}",
|
||||
new
|
||||
{
|
||||
logs = logs,
|
||||
exception = e.ToString()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Internal method to execute the script using Roslyn.
|
||||
/// </summary>
|
||||
private static object ExecuteScriptInternal(string script, string[] imports)
|
||||
{
|
||||
try
|
||||
{
|
||||
// Collect assembly references
|
||||
var references = new List<System.Reflection.Assembly>
|
||||
{
|
||||
typeof(UnityEngine.Debug).Assembly,
|
||||
typeof(UnityEditor.EditorApplication).Assembly
|
||||
};
|
||||
|
||||
// Add Assembly-CSharp if it exists (runtime user code)
|
||||
var assemblyCSharp = System.AppDomain.CurrentDomain.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name == "Assembly-CSharp");
|
||||
if (assemblyCSharp != null)
|
||||
{
|
||||
references.Add(assemblyCSharp);
|
||||
}
|
||||
|
||||
// Add Assembly-CSharp-Editor if it exists (editor user code)
|
||||
var assemblyCSharpEditor = System.AppDomain.CurrentDomain.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name == "Assembly-CSharp-Editor");
|
||||
if (assemblyCSharpEditor != null)
|
||||
{
|
||||
references.Add(assemblyCSharpEditor);
|
||||
}
|
||||
|
||||
// Add Unity.InputSystem if it exists (Input System package)
|
||||
var inputSystemAssembly = System.AppDomain.CurrentDomain.GetAssemblies()
|
||||
.FirstOrDefault(a => a.GetName().Name == "Unity.InputSystem");
|
||||
if (inputSystemAssembly != null)
|
||||
{
|
||||
references.Add(inputSystemAssembly);
|
||||
}
|
||||
|
||||
// Create script options with imports
|
||||
var options = ScriptOptions.Default
|
||||
.WithReferences(references)
|
||||
.WithImports(imports);
|
||||
|
||||
// Execute the script synchronously
|
||||
var scriptTask = CSharpScript.EvaluateAsync(script, options);
|
||||
|
||||
// Wait for the task to complete
|
||||
scriptTask.Wait();
|
||||
|
||||
return scriptTask.Result;
|
||||
}
|
||||
catch (AggregateException ae)
|
||||
{
|
||||
// Unwrap AggregateException to get the actual exception
|
||||
if (ae.InnerException != null)
|
||||
{
|
||||
throw ae.InnerException;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Starts capturing Unity logs.
|
||||
/// </summary>
|
||||
private static void StartLogCapture(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
_isCapturingLogs = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_capturedLogs.Clear();
|
||||
_isCapturingLogs = true;
|
||||
Application.logMessageReceived += OnLogMessageReceived;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops capturing logs and returns the captured log list.
|
||||
/// </summary>
|
||||
private static List<string> StopLogCapture()
|
||||
{
|
||||
Application.logMessageReceived -= OnLogMessageReceived;
|
||||
_isCapturingLogs = false;
|
||||
|
||||
var logs = new List<string>(_capturedLogs);
|
||||
_capturedLogs.Clear();
|
||||
|
||||
return logs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Log message callback handler.
|
||||
/// </summary>
|
||||
private static void OnLogMessageReceived(string logString, string stackTrace, LogType type)
|
||||
{
|
||||
if (!_isCapturingLogs)
|
||||
return;
|
||||
|
||||
var logEntry = new StringBuilder();
|
||||
logEntry.Append($"[{type}] {logString}");
|
||||
|
||||
// Include stack trace for errors and exceptions
|
||||
if ((type == LogType.Error || type == LogType.Exception) && !string.IsNullOrEmpty(stackTrace))
|
||||
{
|
||||
logEntry.Append($"\n{stackTrace}");
|
||||
}
|
||||
|
||||
_capturedLogs.Add(logEntry.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user