attempted ManageScene debugging for hang

main
David Sarno 2025-09-09 19:26:17 -07:00
parent c1bde804d4
commit 46df7250b5
2 changed files with 53 additions and 7 deletions

View File

@ -575,6 +575,10 @@ namespace MCPForUnity.Editor
response = JsonConvert.SerializeObject(errorResponse); response = JsonConvert.SerializeObject(errorResponse);
} }
if (IsDebugEnabled())
{
try { MCPForUnity.Editor.Helpers.McpLog.Info("[MCP] sending framed response", always: false); } catch { }
}
byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(response); byte[] responseBytes = System.Text.Encoding.UTF8.GetBytes(response);
await WriteFrameAsync(stream, responseBytes); await WriteFrameAsync(stream, responseBytes);
} }
@ -858,6 +862,12 @@ namespace MCPForUnity.Editor
if (func == null) return null; if (func == null) return null;
try try
{ {
// If mainThreadId is unknown, assume we're on main thread to avoid blocking the editor.
if (mainThreadId == 0)
{
try { return func(); }
catch (Exception ex) { throw new InvalidOperationException($"Main thread handler error: {ex.Message}", ex); }
}
// If we are already on the main thread, execute directly to avoid deadlocks // If we are already on the main thread, execute directly to avoid deadlocks
try try
{ {
@ -895,13 +905,13 @@ namespace MCPForUnity.Editor
} }
if (captured != null) if (captured != null)
{ {
return Response.Error($"Main thread handler error: {captured.Message}"); throw new InvalidOperationException($"Main thread handler error: {captured.Message}", captured);
} }
return result; return result;
} }
catch (Exception ex) catch (Exception ex)
{ {
return Response.Error($"Failed to invoke on main thread: {ex.Message}"); throw new InvalidOperationException($"Failed to invoke on main thread: {ex.Message}", ex);
} }
} }
@ -969,8 +979,9 @@ namespace MCPForUnity.Editor
// Maps the command type (tool name) to the corresponding handler's static HandleCommand method // Maps the command type (tool name) to the corresponding handler's static HandleCommand method
// Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters // Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters
"manage_script" => ManageScript.HandleCommand(paramsObject), "manage_script" => ManageScript.HandleCommand(paramsObject),
// Run scene operations on the main thread to avoid deadlocks/hangs // Run scene operations on the main thread to avoid deadlocks/hangs (with diagnostics under debug flag)
"manage_scene" => InvokeOnMainThreadWithTimeout(() => ManageScene.HandleCommand(paramsObject), FrameIOTimeoutMs) ?? Response.Error("manage_scene timed out on main thread"), "manage_scene" => HandleManageScene(paramsObject)
?? throw new TimeoutException($"manage_scene timed out after {FrameIOTimeoutMs} ms on main thread"),
"manage_editor" => ManageEditor.HandleCommand(paramsObject), "manage_editor" => ManageEditor.HandleCommand(paramsObject),
"manage_gameobject" => ManageGameObject.HandleCommand(paramsObject), "manage_gameobject" => ManageGameObject.HandleCommand(paramsObject),
"manage_asset" => ManageAsset.HandleCommand(paramsObject), "manage_asset" => ManageAsset.HandleCommand(paramsObject),
@ -1008,6 +1019,23 @@ namespace MCPForUnity.Editor
} }
} }
private static object HandleManageScene(JObject paramsObject)
{
try
{
if (IsDebugEnabled()) Debug.Log("[MCP] manage_scene: dispatching to main thread");
var sw = System.Diagnostics.Stopwatch.StartNew();
var r = InvokeOnMainThreadWithTimeout(() => ManageScene.HandleCommand(paramsObject), FrameIOTimeoutMs);
sw.Stop();
if (IsDebugEnabled()) Debug.Log($"[MCP] manage_scene: completed in {sw.ElapsedMilliseconds} ms");
return r ?? Response.Error("manage_scene returned null (timeout or error)");
}
catch (Exception ex)
{
return Response.Error($"manage_scene dispatch error: {ex.Message}");
}
}
// Helper method to get a summary of parameters for error reporting // Helper method to get a summary of parameters for error reporting
private static string GetParamsSummary(JObject @params) private static string GetParamsSummary(JObject @params)
{ {

View File

@ -21,6 +21,7 @@ namespace MCPForUnity.Editor.Tools
/// </summary> /// </summary>
public static object HandleCommand(JObject @params) public static object HandleCommand(JObject @params)
{ {
try { McpLog.Info("[ManageScene] HandleCommand: start", always: false); } catch { }
string action = @params["action"]?.ToString().ToLower(); string action = @params["action"]?.ToString().ToLower();
string name = @params["name"]?.ToString(); string name = @params["name"]?.ToString();
string path = @params["path"]?.ToString(); // Relative to Assets/ string path = @params["path"]?.ToString(); // Relative to Assets/
@ -76,6 +77,7 @@ namespace MCPForUnity.Editor.Tools
} }
// Route action // Route action
try { McpLog.Info($"[ManageScene] Route action='{action}' name='{name}' path='{path}' buildIndex={(buildIndex.HasValue ? buildIndex.Value.ToString() : "null")}", always: false); } catch { }
switch (action) switch (action)
{ {
case "create": case "create":
@ -98,9 +100,15 @@ namespace MCPForUnity.Editor.Tools
// Save current scene, optionally to a new path // Save current scene, optionally to a new path
return SaveScene(fullPath, relativePath); return SaveScene(fullPath, relativePath);
case "get_hierarchy": case "get_hierarchy":
return GetSceneHierarchy(); try { McpLog.Info("[ManageScene] get_hierarchy: entering", always: false); } catch { }
var gh = GetSceneHierarchy();
try { McpLog.Info("[ManageScene] get_hierarchy: exiting", always: false); } catch { }
return gh;
case "get_active": case "get_active":
return GetActiveSceneInfo(); try { McpLog.Info("[ManageScene] get_active: entering", always: false); } catch { }
var ga = GetActiveSceneInfo();
try { McpLog.Info("[ManageScene] get_active: exiting", always: false); } catch { }
return ga;
case "get_build_settings": case "get_build_settings":
return GetBuildSettingsScenes(); return GetBuildSettingsScenes();
// Add cases for modifying build settings, additive loading, unloading etc. // Add cases for modifying build settings, additive loading, unloading etc.
@ -294,7 +302,9 @@ namespace MCPForUnity.Editor.Tools
{ {
try try
{ {
try { McpLog.Info("[ManageScene] get_active: querying EditorSceneManager.GetActiveScene", always: false); } catch { }
Scene activeScene = EditorSceneManager.GetActiveScene(); Scene activeScene = EditorSceneManager.GetActiveScene();
try { McpLog.Info($"[ManageScene] get_active: got scene valid={activeScene.IsValid()} loaded={activeScene.isLoaded} name='{activeScene.name}'", always: false); } catch { }
if (!activeScene.IsValid()) if (!activeScene.IsValid())
{ {
return Response.Error("No active scene found."); return Response.Error("No active scene found.");
@ -314,6 +324,7 @@ namespace MCPForUnity.Editor.Tools
} }
catch (Exception e) catch (Exception e)
{ {
try { McpLog.Error($"[ManageScene] get_active: exception {e.Message}"); } catch { }
return Response.Error($"Error getting active scene info: {e.Message}"); return Response.Error($"Error getting active scene info: {e.Message}");
} }
} }
@ -348,7 +359,9 @@ namespace MCPForUnity.Editor.Tools
{ {
try try
{ {
try { McpLog.Info("[ManageScene] get_hierarchy: querying EditorSceneManager.GetActiveScene", always: false); } catch { }
Scene activeScene = EditorSceneManager.GetActiveScene(); Scene activeScene = EditorSceneManager.GetActiveScene();
try { McpLog.Info($"[ManageScene] get_hierarchy: got scene valid={activeScene.IsValid()} loaded={activeScene.isLoaded} name='{activeScene.name}'", always: false); } catch { }
if (!activeScene.IsValid() || !activeScene.isLoaded) if (!activeScene.IsValid() || !activeScene.isLoaded)
{ {
return Response.Error( return Response.Error(
@ -356,16 +369,21 @@ namespace MCPForUnity.Editor.Tools
); );
} }
try { McpLog.Info("[ManageScene] get_hierarchy: fetching root objects", always: false); } catch { }
GameObject[] rootObjects = activeScene.GetRootGameObjects(); GameObject[] rootObjects = activeScene.GetRootGameObjects();
try { McpLog.Info($"[ManageScene] get_hierarchy: rootCount={rootObjects?.Length ?? 0}", always: false); } catch { }
var hierarchy = rootObjects.Select(go => GetGameObjectDataRecursive(go)).ToList(); var hierarchy = rootObjects.Select(go => GetGameObjectDataRecursive(go)).ToList();
return Response.Success( var resp = Response.Success(
$"Retrieved hierarchy for scene '{activeScene.name}'.", $"Retrieved hierarchy for scene '{activeScene.name}'.",
hierarchy hierarchy
); );
try { McpLog.Info("[ManageScene] get_hierarchy: success", always: false); } catch { }
return resp;
} }
catch (Exception e) catch (Exception e)
{ {
try { McpLog.Error($"[ManageScene] get_hierarchy: exception {e.Message}"); } catch { }
return Response.Error($"Error getting scene hierarchy: {e.Message}"); return Response.Error($"Error getting scene hierarchy: {e.Message}");
} }
} }