diff --git a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/BufferedFileLogStorage.cs b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/BufferedFileLogStorage.cs index 514bc9fa0..838dca1de 100644 --- a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/BufferedFileLogStorage.cs +++ b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/BufferedFileLogStorage.cs @@ -181,19 +181,19 @@ protected override LogEntry[] QueryInternal( { if (entry.Timestamp < cutoffTime) { - 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(); @@ -201,7 +201,17 @@ protected override LogEntry[] QueryInternal( 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; } ~BufferedFileLogStorage() => Dispose(); diff --git a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/FileLogStorage.cs b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/FileLogStorage.cs index 68caf8192..cd948525e 100644 --- a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/FileLogStorage.cs +++ b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Unity/Logs/FileLogStorage.cs @@ -341,6 +341,13 @@ protected virtual LogEntry[] QueryInternal( .Reverse() .ToArray(); + // Clear stack traces if not requested + if (!includeStackTrace) + { + foreach (var log in filteredLogs) + log.StackTrace = null; + } + return filteredLogs; } } diff --git a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.cs b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.cs index 483b059d9..4ce1746a9 100644 --- a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.cs +++ b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.cs @@ -1,16 +1,16 @@ /* -┌──────────────────────────────────────────────────────────────────┐ -│ 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. + */ #nullable enable #if UNITY_EDITOR && UNITY_6000_5_OR_NEWER +using System.Linq; using com.IvanMurzak.McpPlugin.Common; +using UnityEngine; using UnityEngine.SceneManagement; namespace com.IvanMurzak.Unity.MCP.Runtime.Utils @@ -18,36 +18,57 @@ namespace com.IvanMurzak.Unity.MCP.Runtime.Utils public static partial class GameObjectUtils { /// - /// Find Root GameObject in opened Prefab. Of array of GameObjects in a scene. + /// Find Root GameObject in opened Prefab, active scene, and DontDestroyOnLoad. /// /// Scene for the search, if null the current active scene would be used /// Array of root GameObjects - 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() + .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; diff --git a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.pre-Unity.6.5.cs b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.pre-Unity.6.5.cs index 0d405b0db..d13114822 100644 --- a/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.pre-Unity.6.5.cs +++ b/Unity-MCP-Plugin/Packages/com.ivanmurzak.unity.mcp/Runtime/Utils/GameObjectUtils.Editor.pre-Unity.6.5.cs @@ -1,15 +1,14 @@ /* -┌──────────────────────────────────────────────────────────────────┐ -│ 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. + */ #nullable enable #if UNITY_EDITOR && !UNITY_6000_5_OR_NEWER +using System.Linq; using com.IvanMurzak.McpPlugin.Common; using UnityEngine; using UnityEngine.SceneManagement; @@ -19,7 +18,7 @@ namespace com.IvanMurzak.Unity.MCP.Runtime.Utils public static partial class GameObjectUtils { /// - /// Find Root GameObject in opened Prefab. Of array of GameObjects in a scene. + /// Find Root GameObject in opened Prefab, active scene, and DontDestroyOnLoad. /// /// Scene for the search, if null the current active scene would be used /// Array of root GameObjects @@ -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() + .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