deea7d2a56
Addresses review feedback on the Unity 6.5 GetInstanceID migration: - UnityObjectIdCompatExtensions: drop the reflective method lookup in favor of a simple #if UNITY_6000_5_OR_NEWER / #else split calling GetEntityId() or GetInstanceID() directly. Also wrap the class in the MCPForUnity.Runtime.Helpers namespace. - UnityTypeConverters: remove the reflective EntityIdToObject probe and call EditorUtility.EntityIdToObject(EntityId) directly under the same version gate. Serialize entityID as EntityId.ToULong() rather than ToString(), since Unity's docs explicitly warn that the textual form is not a stable serialization contract. - Drop #pragma warning disable 0619 from 22 files that no longer make any direct calls to obsolete APIs. The remaining 7 files still need it (FindObjectsOfType, InstanceIDToObject fallback) and are left as-is — those deprecations are out of scope for this PR. - Add the MCPForUnity.Runtime.Helpers using to every file that calls GetInstanceIDCompat() now that the extension method lives in a namespace.
286 lines
10 KiB
C#
286 lines
10 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using MCPForUnity.Editor.Helpers;
|
|
using Newtonsoft.Json.Linq;
|
|
using UnityEditor;
|
|
using UnityEngine;
|
|
using MCPForUnity.Runtime.Helpers;
|
|
|
|
namespace MCPForUnity.Editor.Resources.Scene
|
|
{
|
|
/// <summary>
|
|
/// Resource handler for reading GameObject data.
|
|
/// Provides read-only access to GameObject information without component serialization.
|
|
///
|
|
/// URI: unity://scene/gameobject/{instanceID}
|
|
/// </summary>
|
|
[McpForUnityResource("get_gameobject")]
|
|
public static class GameObjectResource
|
|
{
|
|
public static object HandleCommand(JObject @params)
|
|
{
|
|
if (@params == null)
|
|
{
|
|
return new ErrorResponse("Parameters cannot be null.");
|
|
}
|
|
|
|
// Get instance ID from params
|
|
int? instanceID = null;
|
|
|
|
var idToken = @params["instanceID"] ?? @params["instance_id"] ?? @params["id"];
|
|
if (idToken != null)
|
|
{
|
|
instanceID = ParamCoercion.CoerceInt(idToken, -1);
|
|
if (instanceID == -1)
|
|
{
|
|
instanceID = null;
|
|
}
|
|
}
|
|
|
|
if (!instanceID.HasValue)
|
|
{
|
|
return new ErrorResponse("'instanceID' parameter is required.");
|
|
}
|
|
|
|
try
|
|
{
|
|
var go = GameObjectLookup.ResolveInstanceID(instanceID.Value) as GameObject;
|
|
if (go == null)
|
|
{
|
|
return new ErrorResponse($"GameObject with instance ID {instanceID} not found.");
|
|
}
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
data = SerializeGameObject(go)
|
|
};
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
McpLog.Error($"[GameObjectResource] Error getting GameObject: {e}");
|
|
return new ErrorResponse($"Error getting GameObject: {e.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Serializes a GameObject without component details.
|
|
/// For component data, use GetComponents or GetComponent resources.
|
|
/// </summary>
|
|
public static object SerializeGameObject(GameObject go)
|
|
{
|
|
if (go == null)
|
|
return null;
|
|
|
|
var transform = go.transform;
|
|
|
|
// Get component type names (not full serialization)
|
|
var componentTypes = go.GetComponents<Component>()
|
|
.Where(c => c != null)
|
|
.Select(c => c.GetType().Name)
|
|
.ToList();
|
|
|
|
// Get children instance IDs (not full serialization)
|
|
var childrenIds = new List<int>();
|
|
foreach (Transform child in transform)
|
|
{
|
|
childrenIds.Add(child.gameObject.GetInstanceIDCompat());
|
|
}
|
|
|
|
return new
|
|
{
|
|
instanceID = go.GetInstanceIDCompat(),
|
|
name = go.name,
|
|
tag = go.tag,
|
|
layer = go.layer,
|
|
layerName = LayerMask.LayerToName(go.layer),
|
|
active = go.activeSelf,
|
|
activeInHierarchy = go.activeInHierarchy,
|
|
isStatic = go.isStatic,
|
|
transform = new
|
|
{
|
|
position = SerializeVector3(transform.position),
|
|
localPosition = SerializeVector3(transform.localPosition),
|
|
rotation = SerializeVector3(transform.eulerAngles),
|
|
localRotation = SerializeVector3(transform.localEulerAngles),
|
|
scale = SerializeVector3(transform.localScale),
|
|
lossyScale = SerializeVector3(transform.lossyScale)
|
|
},
|
|
parent = transform.parent != null ? transform.parent.gameObject.GetInstanceIDCompat() : (int?)null,
|
|
children = childrenIds,
|
|
componentTypes = componentTypes,
|
|
path = GameObjectLookup.GetGameObjectPath(go)
|
|
};
|
|
}
|
|
|
|
private static object SerializeVector3(Vector3 v)
|
|
{
|
|
return new { x = v.x, y = v.y, z = v.z };
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resource handler for reading all components on a GameObject.
|
|
///
|
|
/// URI: unity://scene/gameobject/{instanceID}/components
|
|
/// </summary>
|
|
[McpForUnityResource("get_gameobject_components")]
|
|
public static class GameObjectComponentsResource
|
|
{
|
|
public static object HandleCommand(JObject @params)
|
|
{
|
|
if (@params == null)
|
|
{
|
|
return new ErrorResponse("Parameters cannot be null.");
|
|
}
|
|
|
|
var idToken = @params["instanceID"] ?? @params["instance_id"] ?? @params["id"];
|
|
int instanceID = ParamCoercion.CoerceInt(idToken, -1);
|
|
if (instanceID == -1)
|
|
{
|
|
return new ErrorResponse("'instanceID' parameter is required.");
|
|
}
|
|
|
|
// Pagination parameters
|
|
int pageSize = ParamCoercion.CoerceInt(@params["pageSize"] ?? @params["page_size"], 25);
|
|
int cursor = ParamCoercion.CoerceInt(@params["cursor"], 0);
|
|
bool includeProperties = ParamCoercion.CoerceBool(@params["includeProperties"] ?? @params["include_properties"], true);
|
|
|
|
pageSize = Mathf.Clamp(pageSize, 1, 100);
|
|
|
|
try
|
|
{
|
|
var go = GameObjectLookup.ResolveInstanceID(instanceID) as GameObject;
|
|
if (go == null)
|
|
{
|
|
return new ErrorResponse($"GameObject with instance ID {instanceID} not found.");
|
|
}
|
|
|
|
var allComponents = go.GetComponents<Component>().Where(c => c != null).ToList();
|
|
int total = allComponents.Count;
|
|
|
|
var pagedComponents = allComponents.Skip(cursor).Take(pageSize).ToList();
|
|
|
|
var componentData = new List<object>();
|
|
foreach (var component in pagedComponents)
|
|
{
|
|
if (includeProperties)
|
|
{
|
|
componentData.Add(GameObjectSerializer.GetComponentData(component));
|
|
}
|
|
else
|
|
{
|
|
componentData.Add(new
|
|
{
|
|
typeName = component.GetType().FullName,
|
|
instanceID = component.GetInstanceIDCompat()
|
|
});
|
|
}
|
|
}
|
|
|
|
int? nextCursor = cursor + pagedComponents.Count < total ? cursor + pagedComponents.Count : (int?)null;
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
data = new
|
|
{
|
|
gameObjectID = instanceID,
|
|
gameObjectName = go.name,
|
|
components = componentData,
|
|
cursor = cursor,
|
|
pageSize = pageSize,
|
|
nextCursor = nextCursor,
|
|
totalCount = total,
|
|
hasMore = nextCursor.HasValue,
|
|
includeProperties = includeProperties
|
|
}
|
|
};
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
McpLog.Error($"[GameObjectComponentsResource] Error getting components: {e}");
|
|
return new ErrorResponse($"Error getting components: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resource handler for reading a single component on a GameObject.
|
|
///
|
|
/// URI: unity://scene/gameobject/{instanceID}/component/{componentName}
|
|
/// </summary>
|
|
[McpForUnityResource("get_gameobject_component")]
|
|
public static class GameObjectComponentResource
|
|
{
|
|
public static object HandleCommand(JObject @params)
|
|
{
|
|
if (@params == null)
|
|
{
|
|
return new ErrorResponse("Parameters cannot be null.");
|
|
}
|
|
|
|
var idToken = @params["instanceID"] ?? @params["instance_id"] ?? @params["id"];
|
|
int instanceID = ParamCoercion.CoerceInt(idToken, -1);
|
|
if (instanceID == -1)
|
|
{
|
|
return new ErrorResponse("'instanceID' parameter is required.");
|
|
}
|
|
|
|
string componentName = ParamCoercion.CoerceString(@params["componentName"] ?? @params["component_name"] ?? @params["component"], null);
|
|
if (string.IsNullOrEmpty(componentName))
|
|
{
|
|
return new ErrorResponse("'componentName' parameter is required.");
|
|
}
|
|
|
|
try
|
|
{
|
|
var go = GameObjectLookup.ResolveInstanceID(instanceID) as GameObject;
|
|
if (go == null)
|
|
{
|
|
return new ErrorResponse($"GameObject with instance ID {instanceID} not found.");
|
|
}
|
|
|
|
// Find the component by type name
|
|
Component targetComponent = null;
|
|
foreach (var component in go.GetComponents<Component>())
|
|
{
|
|
if (component == null) continue;
|
|
|
|
var typeName = component.GetType().Name;
|
|
var fullTypeName = component.GetType().FullName;
|
|
|
|
if (string.Equals(typeName, componentName, StringComparison.OrdinalIgnoreCase) ||
|
|
string.Equals(fullTypeName, componentName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
targetComponent = component;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (targetComponent == null)
|
|
{
|
|
return new ErrorResponse($"Component '{componentName}' not found on GameObject '{go.name}'.");
|
|
}
|
|
|
|
return new
|
|
{
|
|
success = true,
|
|
data = new
|
|
{
|
|
gameObjectID = instanceID,
|
|
gameObjectName = go.name,
|
|
component = GameObjectSerializer.GetComponentData(targetComponent)
|
|
}
|
|
};
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
McpLog.Error($"[GameObjectComponentResource] Error getting component: {e}");
|
|
return new ErrorResponse($"Error getting component: {e.Message}");
|
|
}
|
|
}
|
|
}
|
|
}
|