From 46df7250b514b34b5a645edc0915d2964728e695 Mon Sep 17 00:00:00 2001 From: David Sarno Date: Tue, 9 Sep 2025 19:26:17 -0700 Subject: [PATCH] attempted ManageScene debugging for hang --- UnityMcpBridge/Editor/MCPForUnityBridge.cs | 36 +++++++++++++++++++--- UnityMcpBridge/Editor/Tools/ManageScene.cs | 24 +++++++++++++-- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/UnityMcpBridge/Editor/MCPForUnityBridge.cs b/UnityMcpBridge/Editor/MCPForUnityBridge.cs index 5ee3c0c..23bdc12 100644 --- a/UnityMcpBridge/Editor/MCPForUnityBridge.cs +++ b/UnityMcpBridge/Editor/MCPForUnityBridge.cs @@ -575,6 +575,10 @@ namespace MCPForUnity.Editor 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); await WriteFrameAsync(stream, responseBytes); } @@ -858,6 +862,12 @@ namespace MCPForUnity.Editor if (func == null) return null; 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 try { @@ -895,13 +905,13 @@ namespace MCPForUnity.Editor } if (captured != null) { - return Response.Error($"Main thread handler error: {captured.Message}"); + throw new InvalidOperationException($"Main thread handler error: {captured.Message}", captured); } return result; } 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 // Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters "manage_script" => ManageScript.HandleCommand(paramsObject), - // Run scene operations on the main thread to avoid deadlocks/hangs - "manage_scene" => InvokeOnMainThreadWithTimeout(() => ManageScene.HandleCommand(paramsObject), FrameIOTimeoutMs) ?? Response.Error("manage_scene timed out on main thread"), + // Run scene operations on the main thread to avoid deadlocks/hangs (with diagnostics under debug flag) + "manage_scene" => HandleManageScene(paramsObject) + ?? throw new TimeoutException($"manage_scene timed out after {FrameIOTimeoutMs} ms on main thread"), "manage_editor" => ManageEditor.HandleCommand(paramsObject), "manage_gameobject" => ManageGameObject.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 private static string GetParamsSummary(JObject @params) { diff --git a/UnityMcpBridge/Editor/Tools/ManageScene.cs b/UnityMcpBridge/Editor/Tools/ManageScene.cs index fbf0b7e..d3ae3d1 100644 --- a/UnityMcpBridge/Editor/Tools/ManageScene.cs +++ b/UnityMcpBridge/Editor/Tools/ManageScene.cs @@ -21,6 +21,7 @@ namespace MCPForUnity.Editor.Tools /// public static object HandleCommand(JObject @params) { + try { McpLog.Info("[ManageScene] HandleCommand: start", always: false); } catch { } string action = @params["action"]?.ToString().ToLower(); string name = @params["name"]?.ToString(); string path = @params["path"]?.ToString(); // Relative to Assets/ @@ -76,6 +77,7 @@ namespace MCPForUnity.Editor.Tools } // 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) { case "create": @@ -98,9 +100,15 @@ namespace MCPForUnity.Editor.Tools // Save current scene, optionally to a new path return SaveScene(fullPath, relativePath); 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": - 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": return GetBuildSettingsScenes(); // Add cases for modifying build settings, additive loading, unloading etc. @@ -294,7 +302,9 @@ namespace MCPForUnity.Editor.Tools { try { + try { McpLog.Info("[ManageScene] get_active: querying EditorSceneManager.GetActiveScene", always: false); } catch { } 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()) { return Response.Error("No active scene found."); @@ -314,6 +324,7 @@ namespace MCPForUnity.Editor.Tools } catch (Exception e) { + try { McpLog.Error($"[ManageScene] get_active: exception {e.Message}"); } catch { } return Response.Error($"Error getting active scene info: {e.Message}"); } } @@ -348,7 +359,9 @@ namespace MCPForUnity.Editor.Tools { try { + try { McpLog.Info("[ManageScene] get_hierarchy: querying EditorSceneManager.GetActiveScene", always: false); } catch { } 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) { 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(); + try { McpLog.Info($"[ManageScene] get_hierarchy: rootCount={rootObjects?.Length ?? 0}", always: false); } catch { } var hierarchy = rootObjects.Select(go => GetGameObjectDataRecursive(go)).ToList(); - return Response.Success( + var resp = Response.Success( $"Retrieved hierarchy for scene '{activeScene.name}'.", hierarchy ); + try { McpLog.Info("[ManageScene] get_hierarchy: success", always: false); } catch { } + return resp; } catch (Exception e) { + try { McpLog.Error($"[ManageScene] get_hierarchy: exception {e.Message}"); } catch { } return Response.Error($"Error getting scene hierarchy: {e.Message}"); } }