fix: [AI] includeStackTrace not working & DontDestroyOnLoad objects missing in gameobject-find#836
Conversation
When searching by name or path, FindRootGameObjects() only returned active scene root objects. Games using DontDestroyOnLoad for UI canvases could not be found by gameobject-find. Now includes DontDestroyOnLoad root objects when in Play mode. Co-authored-by: AI (Codex) <noreply@openai.com>
- 修复 FileLogStorage.QueryInternal() 中 includeStackTrace 参数未被使用的问题 - 修复 BufferedFileLogStorage.QueryInternal() 中多个返回点未清除 StackTrace 的问题 - 添加 ClearStackTraceIfNeeded() 辅助方法确保行为一致 当 includeStackTrace 为 false 时,返回的 LogEntry 中 StackTrace 属性现在会被正确清除为 null
IvanMurzak
left a comment
There was a problem hiding this comment.
Probably weak AI model was used. This pull request has low quality.
| * Author: Ivan Murzak (https://github.com/IvanMurzak) | ||
| * Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) | ||
| * Copyright (c) 2025 Ivan Murzak | ||
| * Licensed under the Apache License, Version 2.0. | ||
| * See the LICENSE file in the project root for more information. | ||
| */ |
| * Author: Ivan Murzak (https://github.com/IvanMurzak) | ||
| * Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) | ||
| * Copyright (c) 2025 Ivan Murzak | ||
| * Licensed under the Apache License, Version 2.0. | ||
| * See the LICENSE file in the project root for more information. | ||
| */ |
| using System.Linq; | ||
| using com.IvanMurzak.McpPlugin.Common; | ||
| using UnityEngine; | ||
| using UnityEngine.SceneManagement; | ||
|
|
||
| namespace com.IvanMurzak.Unity.MCP.Runtime.Utils | ||
| { | ||
| public static partial class GameObjectUtils | ||
| { | ||
| /// <summary> | ||
| /// Find Root GameObject in opened Prefab. Of array of GameObjects in a scene. | ||
| /// Find Root GameObject in opened Prefab, active scene, and DontDestroyOnLoad. | ||
| /// </summary> | ||
| /// <param name="scene">Scene for the search, if null the current active scene would be used</param> | ||
| /// <returns>Array of root GameObjects</returns> | ||
| public static UnityEngine.GameObject[] FindRootGameObjects(Scene? scene = null) | ||
| public static GameObject[] FindRootGameObjects(Scene? scene = null) | ||
| { | ||
| var prefabStage = UnityEditor.SceneManagement.PrefabStageUtility.GetCurrentPrefabStage(); | ||
| if (prefabStage != null) | ||
| return prefabStage.prefabContentsRoot.MakeArray(); | ||
|
|
||
| GameObject[] rootGos; | ||
|
|
||
| if (scene == null) | ||
| { | ||
| var rootGos = UnityEditor.SceneManagement.EditorSceneManager | ||
| rootGos = UnityEditor.SceneManagement.EditorSceneManager | ||
| .GetActiveScene() | ||
| .GetRootGameObjects(); | ||
|
|
||
| return rootGos; | ||
| } | ||
| else | ||
| { | ||
| return scene.Value.GetRootGameObjects(); | ||
| rootGos = scene.Value.GetRootGameObjects(); | ||
| } | ||
|
|
||
| // Include DontDestroyOnLoad root objects (only available at runtime) | ||
| if (UnityEditor.EditorApplication.isPlaying) | ||
| { | ||
| var ddolRoots = Resources.FindObjectsOfTypeAll<GameObject>() | ||
| .Where(go => go.scene.name == "DontDestroyOnLoad" | ||
| && go.transform.parent == null | ||
| && (go.hideFlags == HideFlags.None || go.hideFlags == HideFlags.HideInHierarchy)) | ||
| .ToArray(); | ||
|
|
||
| if (ddolRoots.Length > 0) | ||
| { | ||
| var combined = new GameObject[rootGos.Length + ddolRoots.Length]; | ||
| rootGos.CopyTo(combined, 0); | ||
| ddolRoots.CopyTo(combined, rootGos.Length); | ||
| return combined; | ||
| } | ||
| } | ||
|
|
||
| return rootGos; | ||
| } | ||
| public static UnityEngine.GameObject? FindByInstanceID(UnityEngine.EntityId instanceID) | ||
|
|
||
| public static GameObject? FindByInstanceID(EntityId instanceID) | ||
| { | ||
| if (instanceID == UnityEngine.EntityId.None) | ||
| if (instanceID == EntityId.None) | ||
| return null; | ||
|
|
||
| var obj = UnityEditor.EditorUtility.EntityIdToObject(instanceID); | ||
| if (obj is not UnityEngine.GameObject go) | ||
| if (obj is not GameObject go) | ||
| return null; | ||
|
|
||
| return go; |
There was a problem hiding this comment.
GameObject logic should not be included in the PR about logs stacktrace
| @@ -19,7 +18,7 @@ namespace com.IvanMurzak.Unity.MCP.Runtime.Utils | |||
| public static partial class GameObjectUtils | |||
| { | |||
| /// <summary> | |||
| /// Find Root GameObject in opened Prefab. Of array of GameObjects in a scene. | |||
| /// Find Root GameObject in opened Prefab, active scene, and DontDestroyOnLoad. | |||
| /// </summary> | |||
| /// <param name="scene">Scene for the search, if null the current active scene would be used</param> | |||
| /// <returns>Array of root GameObjects</returns> | |||
| @@ -29,26 +28,47 @@ public static GameObject[] FindRootGameObjects(Scene? scene = null) | |||
| if (prefabStage != null) | |||
| return prefabStage.prefabContentsRoot.MakeArray(); | |||
|
|
|||
| GameObject[] rootGos; | |||
|
|
|||
| if (scene == null) | |||
| { | |||
| var rootGos = UnityEditor.SceneManagement.EditorSceneManager | |||
| rootGos = UnityEditor.SceneManagement.EditorSceneManager | |||
| .GetActiveScene() | |||
| .GetRootGameObjects(); | |||
|
|
|||
| return rootGos; | |||
| } | |||
| else | |||
| { | |||
| return scene.Value.GetRootGameObjects(); | |||
| rootGos = scene.Value.GetRootGameObjects(); | |||
| } | |||
|
|
|||
| // Include DontDestroyOnLoad root objects (only available at runtime) | |||
| if (UnityEditor.EditorApplication.isPlaying) | |||
| { | |||
| var ddolRoots = Resources.FindObjectsOfTypeAll<GameObject>() | |||
| .Where(go => go.scene.name == "DontDestroyOnLoad" | |||
| && go.transform.parent == null | |||
| && (go.hideFlags == HideFlags.None || go.hideFlags == HideFlags.HideInHierarchy)) | |||
| .ToArray(); | |||
|
|
|||
| if (ddolRoots.Length > 0) | |||
| { | |||
| var combined = new GameObject[rootGos.Length + ddolRoots.Length]; | |||
| rootGos.CopyTo(combined, 0); | |||
| ddolRoots.CopyTo(combined, rootGos.Length); | |||
| return combined; | |||
| } | |||
| } | |||
|
|
|||
| return rootGos; | |||
| } | |||
|
|
|||
| public static GameObject? FindByInstanceID(int instanceID) | |||
| { | |||
| if (instanceID == 0) | |||
| return null; | |||
|
|
|||
| #if UNITY_6000_3_OR_NEWER | |||
| var obj = UnityEditor.EditorUtility.EntityIdToObject((UnityEngine.EntityId)instanceID); | |||
| var obj = UnityEditor.EditorUtility.EntityIdToObject((EntityId)instanceID); | |||
| #else | |||
| var obj = UnityEditor.EditorUtility.InstanceIDToObject(instanceID); | |||
| #endif | |||
There was a problem hiding this comment.
GameObject logic should not be included in the PR about logs stacktrace
| return null; | ||
|
|
||
| #if UNITY_6000_3_OR_NEWER | ||
| var obj = UnityEditor.EditorUtility.EntityIdToObject((UnityEngine.EntityId)instanceID); |
There was a problem hiding this comment.
UnityEngine namespace should be explicitly set in each line where a type is used.
|
|
||
| public static GameObject? FindByInstanceID(EntityId instanceID) | ||
| { | ||
| if (instanceID == UnityEngine.EntityId.None) |
There was a problem hiding this comment.
UnityEngine namespace should be explicitly set in each line where a type is used.
| return result.AsEnumerable().Reverse().ToArray(); | ||
| return ClearStackTraceIfNeeded(result.AsEnumerable().Reverse().ToArray(), includeStackTrace); | ||
| } | ||
| } | ||
|
|
||
| result.Add(entry); | ||
| if (result.Count >= maxEntries) | ||
| return result.AsEnumerable().Reverse().ToArray(); | ||
| return ClearStackTraceIfNeeded(result.AsEnumerable().Reverse().ToArray(), includeStackTrace); | ||
| } | ||
|
|
||
| // 2. Exit if we already have enough entries | ||
| var neededLogsCount = maxEntries - result.Count; | ||
| if (neededLogsCount <= 0) | ||
| return result.AsEnumerable().Reverse().ToArray(); | ||
| return ClearStackTraceIfNeeded(result.AsEnumerable().Reverse().ToArray(), includeStackTrace); | ||
|
|
||
| result.Reverse(); | ||
|
|
||
| // 3. Get from file | ||
| var fileEntries = base.QueryInternal(neededLogsCount, logTypeFilter, includeStackTrace, lastMinutes); | ||
| result.AddRange(fileEntries); | ||
|
|
||
| return result.ToArray(); | ||
| return ClearStackTraceIfNeeded(result.ToArray(), includeStackTrace); | ||
| } | ||
|
|
||
| private static LogEntry[] ClearStackTraceIfNeeded(LogEntry[] entries, bool includeStackTrace) | ||
| { | ||
| if (!includeStackTrace) | ||
| { | ||
| foreach (var log in entries) | ||
| log.StackTrace = null; | ||
| } | ||
| return entries; | ||
| } |
There was a problem hiding this comment.
This implementation has a weak quality, because it creates an array and then tries to modify after creation. It would be more efficient to use LINQ to generate the proper data using a proper query.
And ideally need to minimize the amount of places where this should be applied to 1, if that is possible.
Test Results 12 files 624 suites 49m 35s ⏱️ For more details on these failures, see this check. Results for commit 35548c5. |
Changes
1. Fix includeStackTrace=false not working in console-get-logs
Problem: The
includeStackTraceparameter in theconsole-get-logstool does not work when set tofalse. Logentries still contain StackTrace information even when explicitly disabled.
Root Cause: The
includeStackTraceparameter is passed through the entire call chain but never actually used tofilter out the
StackTraceproperty:FileLogStorage.QueryInternal()receives the parameter but never uses itBufferedFileLogStorage.QueryInternal()has multiple return points that don't handle the parameterFix:
FileLogStorage.QueryInternal()to clearStackTracebased onincludeStackTracebefore returning resultsBufferedFileLogStorage.QueryInternal()to handleincludeStackTraceat all return pointsClearStackTraceIfNeeded()helper method to ensure consistent behavior2. Fix DontDestroyOnLoad objects missing in gameobject-find search scope
Problem: When searching by name or path,
FindRootGameObjects()only returned active scene root objects. Games usingDontDestroyOnLoadfor UI canvases could not be found bygameobject-find.Fix: Now includes
DontDestroyOnLoadroot objects when in Play mode.Testing
console-get-logs
includeStackTrace: false→StackTraceisnullincludeStackTrace: true→StackTracecontains stack trace informationgameobject-find
DontDestroyOnLoadare now discoverable when searching in Play mode