From c56337ec030f9781c511ce6bddc39904082c7b84 Mon Sep 17 00:00:00 2001 From: Shutong Wu <51266340+Scriptwonder@users.noreply.github.com> Date: Tue, 26 May 2026 16:13:00 +0800 Subject: [PATCH] refactor: drop defensive scaffolding made obsolete by module dep declarations (#1122) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since #1122 declared the Physics 2D, Screen Capture, Image Conversion, etc. built-in modules as required deps in package.json, Unity Package Manager now keeps them enabled while MCP for Unity is installed. Two defensive layers that protected against the now-impossible "module disabled" case become dead code, plus one sub-floor preprocessor gate that was always-true on our declared `unity: 2021.3` floor. Tier 1A — UnityPhysicsCompat.cs - Revert `Type.GetType("UnityEngine.Physics2D, UnityEngine.Physics2DModule")` to `typeof(Physics2D)`. The property-level reflection (for `autoSyncTransforms` deprecation in Unity 6.x) stays — that's a different concern. Tier 1B — TrailControl.cs - Strip `#if UNITY_2021_1_OR_NEWER` gate. Package floor is 2021.3 per package.json, so the `#else` branch ("AddPosition requires Unity 2021.1+") is unreachable. Tier 2 — ScreenshotUtility.cs + callers - Remove `IsScreenCaptureModuleAvailable` property, `ScreenCaptureModuleNotAvailableError` constant, `InvokeCaptureScreenshotAsTexture` helper, and the `s_captureScreenshotMethod` / `s_captureScreenshotAsTextureMethod` / `s_screenCaptureModuleAvailable` reflection caches. - Replace reflective `MethodInfo.Invoke` calls with direct `ScreenCapture.X` calls in `CaptureToProjectFolder`, `CaptureComposited`, and the `ScreenshotCapturer` MonoBehaviour. - Camera-based fallback path (`FindAvailableCamera` + `CaptureFromCameraToProjectFolder`) is preserved — it still handles transient null returns from `ScreenCapture` inside `CaptureComposited`. - Drop pre-flight `if (!IsScreenCaptureModuleAvailable)` gates in `ManageUI.cs` (UI render) and `ManageScene.cs` (screenshot path). - `ProjectInfo` resource: `screenCapture` field is now an invariant `true` (preserves API shape for `mcpforunity://project/info` consumers). Net: 144 lines removed, 15 added. No new tests — the changes are pure removal of code paths that cannot fire with the current deps declared. Related: #1160, #1122. --- .../Editor/Resources/Project/ProjectInfo.cs | 2 +- MCPForUnity/Editor/Tools/ManageScene.cs | 19 +-- MCPForUnity/Editor/Tools/ManageUI.cs | 6 - MCPForUnity/Editor/Tools/Vfx/TrailControl.cs | 4 - .../Runtime/Helpers/ScreenshotUtility.cs | 111 ++---------------- .../Runtime/Helpers/UnityPhysicsCompat.cs | 17 +-- 6 files changed, 15 insertions(+), 144 deletions(-) diff --git a/MCPForUnity/Editor/Resources/Project/ProjectInfo.cs b/MCPForUnity/Editor/Resources/Project/ProjectInfo.cs index cc1502f21..857f6c6c1 100644 --- a/MCPForUnity/Editor/Resources/Project/ProjectInfo.cs +++ b/MCPForUnity/Editor/Resources/Project/ProjectInfo.cs @@ -38,7 +38,7 @@ public static object HandleCommand(JObject @params) textmeshpro = IsPackageInstalled("com.unity.textmeshpro"), inputsystem = IsPackageInstalled("com.unity.inputsystem"), uiToolkit = true, - screenCapture = MCPForUnity.Runtime.Helpers.ScreenshotUtility.IsScreenCaptureModuleAvailable, + screenCapture = true, } }; diff --git a/MCPForUnity/Editor/Tools/ManageScene.cs b/MCPForUnity/Editor/Tools/ManageScene.cs index aa3c78602..c3c66b3fa 100644 --- a/MCPForUnity/Editor/Tools/ManageScene.cs +++ b/MCPForUnity/Editor/Tools/ManageScene.cs @@ -678,24 +678,9 @@ private static object CaptureScreenshot(SceneCommand cmd) return new SuccessResponse(message, BuildScreenshotResponseData(result, targetCamera.name, includeImage)); } - // Default path: use ScreenCapture API if available, camera fallback otherwise - bool screenCaptureAvailable = ScreenshotUtility.IsScreenCaptureModuleAvailable; + // Default path: ScreenCapture API for 2022.1+, camera fallback required on older versions. +#if !UNITY_2022_1_OR_NEWER bool hasCameraFallback = Camera.main != null || UnityFindObjectsCompat.FindAll().Length > 0; - -#if UNITY_2022_1_OR_NEWER - if (!screenCaptureAvailable && !hasCameraFallback) - { - return new ErrorResponse( - "Cannot capture screenshot. The Screen Capture module is not enabled and no Camera was found in the scene. " + - "Please either: (1) Enable the Screen Capture module: Window > Package Manager > Built-in > Screen Capture > Enable, " + - "or (2) Add a Camera to your scene for camera-based fallback capture." - ); - } - if (!screenCaptureAvailable) - { - McpLog.Warn("[ManageScene] Screen Capture module not enabled. Using camera-based fallback."); - } -#else if (!hasCameraFallback) { return new ErrorResponse( diff --git a/MCPForUnity/Editor/Tools/ManageUI.cs b/MCPForUnity/Editor/Tools/ManageUI.cs index 42e79cd27..cad174139 100644 --- a/MCPForUnity/Editor/Tools/ManageUI.cs +++ b/MCPForUnity/Editor/Tools/ManageUI.cs @@ -927,12 +927,6 @@ private static object RenderUI(JObject @params) } // ── Case 2: start a new capture ─────────────────────────────────── - // Verify the ScreenCapture module is enabled before attempting capture. - if (!ScreenshotUtility.IsScreenCaptureModuleAvailable) - { - return new ErrorResponse(ScreenshotUtility.ScreenCaptureModuleNotAvailableError); - } - // Only one capture in flight at a time. If one is already pending, // reject rather than silently overwriting the state. if (s_pendingCaptureStarted) diff --git a/MCPForUnity/Editor/Tools/Vfx/TrailControl.cs b/MCPForUnity/Editor/Tools/Vfx/TrailControl.cs index 01b809c9e..c3b68c9a4 100644 --- a/MCPForUnity/Editor/Tools/Vfx/TrailControl.cs +++ b/MCPForUnity/Editor/Tools/Vfx/TrailControl.cs @@ -24,13 +24,9 @@ public static object Emit(JObject @params) RendererHelpers.EnsureMaterial(tr); -#if UNITY_2021_1_OR_NEWER Vector3 pos = ManageVfxCommon.ParseVector3(@params["position"]); tr.AddPosition(pos); return new { success = true, message = $"Emitted at ({pos.x}, {pos.y}, {pos.z})" }; -#else - return new { success = false, message = "AddPosition requires Unity 2021.1+" }; -#endif } } } diff --git a/MCPForUnity/Runtime/Helpers/ScreenshotUtility.cs b/MCPForUnity/Runtime/Helpers/ScreenshotUtility.cs index 02de15551..a49346ade 100644 --- a/MCPForUnity/Runtime/Helpers/ScreenshotUtility.cs +++ b/MCPForUnity/Runtime/Helpers/ScreenshotUtility.cs @@ -50,57 +50,6 @@ public static class ScreenshotUtility /// or globally via ScreenshotPreferences in the Editor assembly. /// public const string DefaultFolder = "Assets/Screenshots"; - private static bool s_loggedLegacyScreenCaptureFallback; - private static bool? s_screenCaptureModuleAvailable; - private static System.Reflection.MethodInfo s_captureScreenshotMethod; - private static System.Reflection.MethodInfo s_captureScreenshotAsTextureMethod; - - /// - /// Checks if the Screen Capture module (com.unity.modules.screencapture) is enabled. - /// This module can be disabled in Package Manager > Built-in, which removes the ScreenCapture class. - /// - public static bool IsScreenCaptureModuleAvailable - { - get - { - if (!s_screenCaptureModuleAvailable.HasValue) - { - // Check if ScreenCapture type exists (module might be disabled in Package Manager > Built-in) - var screenCaptureType = Type.GetType("UnityEngine.ScreenCapture, UnityEngine.ScreenCaptureModule") - ?? Type.GetType("UnityEngine.ScreenCapture, UnityEngine.CoreModule"); - s_screenCaptureModuleAvailable = screenCaptureType != null; - if (screenCaptureType != null) - { - s_captureScreenshotMethod = screenCaptureType.GetMethod("CaptureScreenshot", - new Type[] { typeof(string), typeof(int) }); - s_captureScreenshotAsTextureMethod = screenCaptureType.GetMethod("CaptureScreenshotAsTexture", - new Type[] { typeof(int) }); - } - } - return s_screenCaptureModuleAvailable.Value; - } - } - - /// - /// Reflective invocation of ScreenCapture.CaptureScreenshotAsTexture(int). Returns - /// null when the Screen Capture module is disabled. Centralised so the only direct - /// reference to ScreenCapture lives here. - /// - internal static Texture2D InvokeCaptureScreenshotAsTexture(int superSize) - { - if (!IsScreenCaptureModuleAvailable || s_captureScreenshotAsTextureMethod == null) - return null; - return s_captureScreenshotAsTextureMethod.Invoke(null, new object[] { superSize }) as Texture2D; - } - - /// - /// Error message to display when Screen Capture module is not available. - /// - public const string ScreenCaptureModuleNotAvailableError = - "The Screen Capture module (com.unity.modules.screencapture) is not enabled. " + - "To use screenshot capture with ScreenCapture API, please enable it in Unity: " + - "Window > Package Manager > Built-in > Screen Capture > Enable. " + - "Alternatively, MCP for Unity will use camera-based capture as a fallback if a Camera exists in the scene."; private static Camera FindAvailableCamera() { @@ -127,44 +76,10 @@ public static ScreenshotCaptureResult CaptureToProjectFolder( bool ensureUniqueFileName = true, string folderOverride = null) { - // Use reflection to call ScreenCapture.CaptureScreenshot so the code compiles - // even when the Screen Capture module (com.unity.modules.screencapture) is disabled. - if (IsScreenCaptureModuleAvailable && s_captureScreenshotMethod != null) - { - ScreenshotCaptureResult result = PrepareCaptureResult(fileName, superSize, ensureUniqueFileName, folderOverride, isAsync: true); - // ScreenCapture.CaptureScreenshot accepts paths relative to the project root. - s_captureScreenshotMethod.Invoke(null, new object[] { result.ProjectRelativePath, result.SuperSize }); - return result; - } - else - { - // Module disabled or unavailable - try camera fallback - Debug.LogWarning("[MCP for Unity] " + ScreenCaptureModuleNotAvailableError); - return CaptureWithCameraFallback(fileName, superSize, ensureUniqueFileName, folderOverride); - } - } - - private static ScreenshotCaptureResult CaptureWithCameraFallback(string fileName, int superSize, bool ensureUniqueFileName, string folderOverride) - { - if (!s_loggedLegacyScreenCaptureFallback) - { - Debug.Log("[MCP for Unity] Using camera-based screenshot capture. " + - "This requires a Camera in the scene. For best results on Unity 2022.1+, ensure the Screen Capture module is enabled: " + - "Window > Package Manager > Built-in > Screen Capture > Enable."); - s_loggedLegacyScreenCaptureFallback = true; - } - - var cam = FindAvailableCamera(); - if (cam == null) - { - throw new InvalidOperationException( - "No camera found to capture screenshot. Camera-based capture requires a Camera in the scene. " + - "Either add a Camera to your scene, or enable the Screen Capture module: " + - "Window > Package Manager > Built-in > Screen Capture > Enable." - ); - } - - return CaptureFromCameraToProjectFolder(cam, fileName, superSize, ensureUniqueFileName, folderOverride: folderOverride); + ScreenshotCaptureResult result = PrepareCaptureResult(fileName, superSize, ensureUniqueFileName, folderOverride, isAsync: true); + // ScreenCapture.CaptureScreenshot accepts paths relative to the project root. + ScreenCapture.CaptureScreenshot(result.ProjectRelativePath, result.SuperSize); + return result; } /// @@ -283,7 +198,7 @@ private static Texture2D CaptureCompositedAfterFrame(int superSize, int timeoutS /// /// Captures a screenshot using ScreenCapture.CaptureScreenshotAsTexture, which captures the /// final composited frame including UI Toolkit overlays, post-processing, etc. - /// Falls back to camera-based capture if ScreenCapture is unavailable. + /// Falls back to camera-based capture if ScreenCapture returns null at runtime. /// public static ScreenshotCaptureResult CaptureComposited( string fileName = null, @@ -293,16 +208,6 @@ public static ScreenshotCaptureResult CaptureComposited( int maxResolution = 0, string folderOverride = null) { - if (!IsScreenCaptureModuleAvailable || s_captureScreenshotAsTextureMethod == null) - { - var fallbackCamera = FindAvailableCamera(); - if (fallbackCamera != null) - return CaptureFromCameraToProjectFolder(fallbackCamera, fileName, superSize, ensureUniqueFileName, - includeImage, maxResolution, folderOverride: folderOverride); - - throw new InvalidOperationException("ScreenCapture module is unavailable and no fallback camera found."); - } - ScreenshotCaptureResult result = PrepareCaptureResult(fileName, superSize, ensureUniqueFileName, folderOverride: folderOverride, isAsync: false); Texture2D tex = null; Texture2D downscaled = null; @@ -315,9 +220,9 @@ public static ScreenshotCaptureResult CaptureComposited( // composited; route through WaitForEndOfFrame instead. tex = Application.isPlaying ? CaptureCompositedAfterFrame(result.SuperSize) - : InvokeCaptureScreenshotAsTexture(result.SuperSize); + : ScreenCapture.CaptureScreenshotAsTexture(result.SuperSize); #else - tex = InvokeCaptureScreenshotAsTexture(result.SuperSize); + tex = ScreenCapture.CaptureScreenshotAsTexture(result.SuperSize); #endif if (tex == null) { @@ -857,7 +762,7 @@ private System.Collections.IEnumerator Start() { yield return new WaitForEndOfFrame(); Texture2D tex = null; - try { tex = ScreenshotUtility.InvokeCaptureScreenshotAsTexture(_superSize); } + try { tex = ScreenCapture.CaptureScreenshotAsTexture(_superSize); } catch (Exception ex) { Debug.LogError($"[MCP for Unity] CaptureScreenshotAsTexture failed: {ex.Message}"); } _onComplete?.Invoke(tex); Destroy(gameObject); diff --git a/MCPForUnity/Runtime/Helpers/UnityPhysicsCompat.cs b/MCPForUnity/Runtime/Helpers/UnityPhysicsCompat.cs index 373b93433..fe3158514 100644 --- a/MCPForUnity/Runtime/Helpers/UnityPhysicsCompat.cs +++ b/MCPForUnity/Runtime/Helpers/UnityPhysicsCompat.cs @@ -17,15 +17,10 @@ namespace MCPForUnity.Runtime.Helpers /// /// We use reflection rather than direct property access so calls stay clean of /// CS0618 warnings AND survive eventual removal of the obsolete property without - /// a recompile of this package. Type lookups go through - /// so this file compiles even when the Physics 2D built-in module is disabled in - /// the Package Manager. + /// a recompile of this package. /// public static class UnityPhysicsCompat { - // Assembly-qualified name — resolved at runtime so the file compiles when the - // Physics 2D built-in module is disabled in the Package Manager. - private const string Physics2DTypeName = "UnityEngine.Physics2D, UnityEngine.Physics2DModule"; /// /// Cross-version description of the 3D physics simulation mode. /// On 2022.2+ this maps onto UnityEngine.SimulationMode; on older @@ -51,13 +46,9 @@ private static PropertyInfo Physics2DAutoSyncProp if (!_physics2DProbed) { _physics2DProbed = true; - var physics2DType = Type.GetType(Physics2DTypeName); - if (physics2DType != null) - { - _physics2DAutoSync = physics2DType.GetProperty( - "autoSyncTransforms", - BindingFlags.Public | BindingFlags.Static); - } + _physics2DAutoSync = typeof(Physics2D).GetProperty( + "autoSyncTransforms", + BindingFlags.Public | BindingFlags.Static); } return _physics2DAutoSync; }