#nullable disable using System; using System.Collections.Generic; using System.Linq; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Tools; using Newtonsoft.Json.Linq; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.SceneManagement; namespace MCPForUnity.Editor.Tools.GameObjects { internal static class ManageGameObjectCommon { internal static GameObject FindObjectInternal(JToken targetToken, string searchMethod, JObject findParams = null) { bool findAll = findParams?["findAll"]?.ToObject() ?? false; if ( targetToken?.Type == JTokenType.Integer || (searchMethod == "by_id" && int.TryParse(targetToken?.ToString(), out _)) ) { findAll = false; } List results = FindObjectsInternal(targetToken, searchMethod, findAll, findParams); return results.Count > 0 ? results[0] : null; } internal static List FindObjectsInternal( JToken targetToken, string searchMethod, bool findAll, JObject findParams = null ) { List results = new List(); string searchTerm = findParams?["searchTerm"]?.ToString() ?? targetToken?.ToString(); bool searchInChildren = findParams?["searchInChildren"]?.ToObject() ?? false; bool searchInactive = findParams?["searchInactive"]?.ToObject() ?? false; if (string.IsNullOrEmpty(searchMethod)) { if (targetToken?.Type == JTokenType.Integer) searchMethod = "by_id"; else if (!string.IsNullOrEmpty(searchTerm) && searchTerm.Contains('/')) searchMethod = "by_path"; else searchMethod = "by_name"; } GameObject rootSearchObject = null; if (searchInChildren && targetToken != null) { rootSearchObject = FindObjectInternal(targetToken, "by_id_or_name_or_path"); if (rootSearchObject == null) { McpLog.Warn($"[ManageGameObject.Find] Root object '{targetToken}' for child search not found."); return results; } } switch (searchMethod) { case "by_id": if (int.TryParse(searchTerm, out int instanceId)) { var allObjects = GetAllSceneObjects(searchInactive); GameObject obj = allObjects.FirstOrDefault(go => go.GetInstanceID() == instanceId); if (obj != null) results.Add(obj); } break; case "by_name": var searchPoolName = rootSearchObject ? rootSearchObject .GetComponentsInChildren(searchInactive) .Select(t => t.gameObject) : GetAllSceneObjects(searchInactive); results.AddRange(searchPoolName.Where(go => go.name == searchTerm)); break; case "by_path": if (rootSearchObject != null) { Transform foundTransform = rootSearchObject.transform.Find(searchTerm); if (foundTransform != null) results.Add(foundTransform.gameObject); } else { var prefabStage = PrefabStageUtility.GetCurrentPrefabStage(); if (prefabStage != null || searchInactive) { // In Prefab Stage, GameObject.Find() doesn't work, need to search manually var allObjects = GetAllSceneObjects(searchInactive); foreach (var go in allObjects) { if (GameObjectLookup.MatchesPath(go, searchTerm)) { results.Add(go); } } } else { var found = GameObject.Find(searchTerm); if (found != null) results.Add(found); } } break; case "by_tag": var searchPoolTag = rootSearchObject ? rootSearchObject .GetComponentsInChildren(searchInactive) .Select(t => t.gameObject) : GetAllSceneObjects(searchInactive); results.AddRange(searchPoolTag.Where(go => go.CompareTag(searchTerm))); break; case "by_layer": var searchPoolLayer = rootSearchObject ? rootSearchObject .GetComponentsInChildren(searchInactive) .Select(t => t.gameObject) : GetAllSceneObjects(searchInactive); if (int.TryParse(searchTerm, out int layerIndex)) { results.AddRange(searchPoolLayer.Where(go => go.layer == layerIndex)); } else { int namedLayer = LayerMask.NameToLayer(searchTerm); if (namedLayer != -1) results.AddRange(searchPoolLayer.Where(go => go.layer == namedLayer)); } break; case "by_component": Type componentType = FindType(searchTerm); if (componentType != null) { IEnumerable searchPoolComp; if (rootSearchObject) { searchPoolComp = rootSearchObject .GetComponentsInChildren(componentType, searchInactive) .Select(c => (c as Component).gameObject); } else { searchPoolComp = UnityEngine.Object.FindObjectsOfType(componentType, searchInactive) .Cast() .Select(c => c.gameObject); } results.AddRange(searchPoolComp.Where(go => go != null)); } else { McpLog.Warn($"[ManageGameObject.Find] Component type not found: {searchTerm}"); } break; case "by_id_or_name_or_path": if (int.TryParse(searchTerm, out int id)) { var allObjectsId = GetAllSceneObjects(true); GameObject objById = allObjectsId.FirstOrDefault(go => go.GetInstanceID() == id); if (objById != null) { results.Add(objById); break; } } // Try path search - in Prefab Stage, GameObject.Find() doesn't work var allObjectsForPath = GetAllSceneObjects(true); GameObject objByPath = allObjectsForPath.FirstOrDefault(go => { return GameObjectLookup.MatchesPath(go, searchTerm); }); if (objByPath != null) { results.Add(objByPath); break; } var allObjectsName = GetAllSceneObjects(true); results.AddRange(allObjectsName.Where(go => go.name == searchTerm)); break; default: McpLog.Warn($"[ManageGameObject.Find] Unknown search method: {searchMethod}"); break; } if (!findAll && results.Count > 1) { return new List { results[0] }; } return results.Distinct().ToList(); } private static IEnumerable GetAllSceneObjects(bool includeInactive) { // Delegate to GameObjectLookup to avoid code duplication and ensure consistent behavior return GameObjectLookup.GetAllSceneObjects(includeInactive); } private static Type FindType(string typeName) { if (ComponentResolver.TryResolve(typeName, out Type resolvedType, out string error)) { return resolvedType; } if (!string.IsNullOrEmpty(error)) { McpLog.Warn($"[FindType] {error}"); } return null; } } }