From 488145371d24034a58aca4a56d65c2c389e9cdde Mon Sep 17 00:00:00 2001 From: DLSINNOCENCE <2089380574@qq.com> Date: Thu, 14 May 2026 17:01:46 +0800 Subject: [PATCH 1/2] Support Unity 6 memory snapshot capture API Unity 6 exposes MemoryProfiler.TakeSnapshot from UnityEngine.CoreModule with CaptureFlags overloads, so the old Unity.MemoryProfiler.Editor reflection path makes memory_take_snapshot report that com.unity.memoryprofiler is missing even when the package is installed. The profiler list and compare actions only inspect snapshot files, so they no longer depend on MemoryProfiler type discovery. Constraint: Unity 6 moved MemoryProfiler APIs into UnityEngine.CoreModule. Rejected: Require com.unity.memoryprofiler editor assembly type discovery for all memory actions | list and compare only need filesystem metadata. Confidence: high Scope-risk: narrow Tested: uv run --extra dev pytest tests/test_manage_profiler.py -v Tested: Reflected Unity 6 MemoryProfiler.TakeSnapshot overloads in GameClient editor. Not-tested: Full Unity package test suite. --- .../Profiler/Operations/MemorySnapshotOps.cs | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs b/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs index 5485ecae5..ab330fb0e 100644 --- a/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs +++ b/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs @@ -11,7 +11,9 @@ namespace MCPForUnity.Editor.Tools.Profiler internal static class MemorySnapshotOps { private static readonly Type MemoryProfilerType = - Type.GetType("Unity.MemoryProfiler.MemoryProfiler, Unity.MemoryProfiler.Editor"); + Type.GetType("Unity.Profiling.Memory.MemoryProfiler, UnityEngine.CoreModule") + ?? Type.GetType("UnityEngine.Profiling.Memory.Experimental.MemoryProfiler, UnityEngine.CoreModule") + ?? Type.GetType("Unity.MemoryProfiler.MemoryProfiler, Unity.MemoryProfiler.Editor"); private static bool HasPackage => MemoryProfilerType != null; @@ -35,11 +37,30 @@ internal static async Task TakeSnapshotAsync(JObject @params) try { var debugScreenCaptureType = Type.GetType( - "Unity.Profiling.Memory.Experimental.DebugScreenCapture, Unity.MemoryProfiler.Editor"); + "Unity.Profiling.DebugScreenCapture, UnityEngine.CoreModule") + ?? Type.GetType("UnityEngine.Profiling.Experimental.DebugScreenCapture, UnityEngine.CoreModule") + ?? Type.GetType("Unity.Profiling.Memory.Experimental.DebugScreenCapture, Unity.MemoryProfiler.Editor"); + var captureFlagsType = Type.GetType( + "Unity.Profiling.Memory.CaptureFlags, UnityEngine.CoreModule") + ?? Type.GetType("UnityEngine.Profiling.Memory.Experimental.CaptureFlags, UnityEngine.CoreModule"); System.Reflection.MethodInfo takeMethod = null; - if (debugScreenCaptureType != null) + if (debugScreenCaptureType != null && captureFlagsType != null) + { + var screenshotCallbackType = typeof(Action<,,>).MakeGenericType( + typeof(string), typeof(bool), debugScreenCaptureType); + takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot", + new[] { typeof(string), typeof(Action), screenshotCallbackType, captureFlagsType }); + } + + if (takeMethod == null && captureFlagsType != null) + { + takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot", + new[] { typeof(string), typeof(Action), captureFlagsType }); + } + + if (takeMethod == null && debugScreenCaptureType != null) { var screenshotCallbackType = typeof(Action<,,>).MakeGenericType( typeof(string), typeof(bool), debugScreenCaptureType); @@ -49,7 +70,6 @@ internal static async Task TakeSnapshotAsync(JObject @params) if (takeMethod == null) { - // Try 2-param overload: TakeSnapshot(string, Action) takeMethod = MemoryProfilerType.GetMethod("TakeSnapshot", new[] { typeof(string), typeof(Action) }); } @@ -76,7 +96,11 @@ internal static async Task TakeSnapshotAsync(JObject @params) }; int paramCount = takeMethod.GetParameters().Length; - if (paramCount == 4) + if (paramCount == 4 && captureFlagsType != null) + takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, Enum.ToObject(captureFlagsType, 0) }); + else if (paramCount == 3 && captureFlagsType != null) + takeMethod.Invoke(null, new object[] { snapshotPath, callback, Enum.ToObject(captureFlagsType, 0) }); + else if (paramCount == 4) takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, 0u }); else if (paramCount == 2) takeMethod.Invoke(null, new object[] { snapshotPath, callback }); @@ -98,9 +122,6 @@ internal static async Task TakeSnapshotAsync(JObject @params) internal static object ListSnapshots(JObject @params) { - if (!HasPackage) - return PackageMissingError(); - var p = new ToolParams(@params); string searchPath = p.Get("search_path"); @@ -141,9 +162,6 @@ internal static object ListSnapshots(JObject @params) internal static object CompareSnapshots(JObject @params) { - if (!HasPackage) - return PackageMissingError(); - var p = new ToolParams(@params); var pathAResult = p.GetRequired("snapshot_a"); if (!pathAResult.IsSuccess) From d9431c5095428e8872ce07abe559b2554cc2bd20 Mon Sep 17 00:00:00 2001 From: DLSINNOCENCE <2089380574@qq.com> Date: Thu, 14 May 2026 17:06:56 +0800 Subject: [PATCH 2/2] Guard memory snapshot overload invocation The MemoryProfiler reflection code now verifies the selected overload's trailing parameter type before invoking it. This keeps Unity 6 CaptureFlags overloads and legacy uint overloads from being mixed in environments where both type families can be discovered. Constraint: Reflection overload selection spans Unity versions with different trailing argument types. Confidence: high Scope-risk: narrow Tested: Patched GameClient PackageCache and verified memory_take_snapshot, memory_list_snapshots, memory_compare_snapshots. Tested: Verified profiler_status, get_frame_timing, get_counters, frame_debugger_get_events, and compare error paths. Not-tested: Full Unity package test suite. --- .../Tools/Profiler/Operations/MemorySnapshotOps.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs b/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs index ab330fb0e..ce3cde997 100644 --- a/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs +++ b/MCPForUnity/Editor/Tools/Profiler/Operations/MemorySnapshotOps.cs @@ -95,12 +95,13 @@ internal static async Task TakeSnapshotAsync(JObject @params) } }; - int paramCount = takeMethod.GetParameters().Length; - if (paramCount == 4 && captureFlagsType != null) + var takeMethodParams = takeMethod.GetParameters(); + int paramCount = takeMethodParams.Length; + if (paramCount == 4 && takeMethodParams[3].ParameterType == captureFlagsType) takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, Enum.ToObject(captureFlagsType, 0) }); - else if (paramCount == 3 && captureFlagsType != null) + else if (paramCount == 3 && takeMethodParams[2].ParameterType == captureFlagsType) takeMethod.Invoke(null, new object[] { snapshotPath, callback, Enum.ToObject(captureFlagsType, 0) }); - else if (paramCount == 4) + else if (paramCount == 4 && takeMethodParams[3].ParameterType == typeof(uint)) takeMethod.Invoke(null, new object[] { snapshotPath, callback, null, 0u }); else if (paramCount == 2) takeMethod.Invoke(null, new object[] { snapshotPath, callback });