#nullable disable using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using MCPForUnity.Editor.Helpers; // For Response class using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.SceneManagement; namespace MCPForUnity.Editor.Tools.GameObjects { /// /// Handles GameObject manipulation within the current scene (CRUD, find, components). /// [McpForUnityTool("manage_gameobject", AutoRegister = false)] public static class ManageGameObject { // --- Main Handler --- public static object HandleCommand(JObject @params) { if (@params == null) { return new ErrorResponse("Parameters cannot be null."); } string action = @params["action"]?.ToString().ToLower(); if (string.IsNullOrEmpty(action)) { return new ErrorResponse("Action parameter is required."); } // Parameters used by various actions JToken targetToken = @params["target"]; // Can be string (name/path) or int (instanceID) string name = @params["name"]?.ToString(); // --- Usability Improvement: Alias 'name' to 'target' for modification actions --- // If 'target' is missing but 'name' is provided, and we aren't creating a new object, // assume the user meant "find object by name". if (targetToken == null && !string.IsNullOrEmpty(name) && action != "create") { targetToken = name; // We don't update @params["target"] because we use targetToken locally mostly, // but some downstream methods might parse @params directly. Let's update @params too for safety. @params["target"] = name; } // ------------------------------------------------------------------------------- string searchMethod = @params["searchMethod"]?.ToString().ToLower(); string tag = @params["tag"]?.ToString(); string layer = @params["layer"]?.ToString(); JToken parentToken = @params["parent"]; // Coerce string JSON to JObject for 'componentProperties' if provided as a JSON string var componentPropsToken = @params["componentProperties"]; if (componentPropsToken != null && componentPropsToken.Type == JTokenType.String) { try { var parsed = JObject.Parse(componentPropsToken.ToString()); @params["componentProperties"] = parsed; } catch (Exception e) { McpLog.Warn($"[ManageGameObject] Could not parse 'componentProperties' JSON string: {e.Message}"); } } // --- Prefab Asset Check --- // Prefab assets require different tools. Only 'create' (instantiation) is valid here. string targetPath = targetToken?.Type == JTokenType.String ? targetToken.ToString() : null; if ( !string.IsNullOrEmpty(targetPath) && targetPath.EndsWith(".prefab", StringComparison.OrdinalIgnoreCase) && action != "create" // Allow prefab instantiation ) { return new ErrorResponse( $"Target '{targetPath}' is a prefab asset. " + $"Use 'manage_asset' with action='modify' for prefab asset modifications, " + $"or 'manage_prefabs' with action='open_stage' to edit the prefab in isolation mode." ); } // --- End Prefab Asset Check --- try { switch (action) { // --- Primary lifecycle actions (kept in manage_gameobject) --- case "create": return GameObjectCreate.Handle(@params); case "modify": return GameObjectModify.Handle(@params, targetToken, searchMethod); case "delete": return GameObjectDelete.Handle(targetToken, searchMethod); case "duplicate": return GameObjectDuplicate.Handle(@params, targetToken, searchMethod); case "move_relative": return GameObjectMoveRelative.Handle(@params, targetToken, searchMethod); default: return new ErrorResponse($"Unknown action: '{action}'."); } } catch (Exception e) { McpLog.Error($"[ManageGameObject] Action '{action}' failed: {e}"); return new ErrorResponse($"Internal error processing action '{action}': {e.Message}"); } } } }