Merge branch 'master' into master
commit
99faaf9684
|
|
@ -0,0 +1,527 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityMcpBridge.Runtime.Serialization; // For Converters
|
||||||
|
|
||||||
|
namespace UnityMcpBridge.Editor.Helpers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Handles serialization of GameObjects and Components for MCP responses.
|
||||||
|
/// Includes reflection helpers and caching for performance.
|
||||||
|
/// </summary>
|
||||||
|
public static class GameObjectSerializer
|
||||||
|
{
|
||||||
|
// --- Data Serialization ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a serializable representation of a GameObject.
|
||||||
|
/// </summary>
|
||||||
|
public static object GetGameObjectData(GameObject go)
|
||||||
|
{
|
||||||
|
if (go == null)
|
||||||
|
return null;
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
name = go.name,
|
||||||
|
instanceID = go.GetInstanceID(),
|
||||||
|
tag = go.tag,
|
||||||
|
layer = go.layer,
|
||||||
|
activeSelf = go.activeSelf,
|
||||||
|
activeInHierarchy = go.activeInHierarchy,
|
||||||
|
isStatic = go.isStatic,
|
||||||
|
scenePath = go.scene.path, // Identify which scene it belongs to
|
||||||
|
transform = new // Serialize transform components carefully to avoid JSON issues
|
||||||
|
{
|
||||||
|
// Serialize Vector3 components individually to prevent self-referencing loops.
|
||||||
|
// The default serializer can struggle with properties like Vector3.normalized.
|
||||||
|
position = new
|
||||||
|
{
|
||||||
|
x = go.transform.position.x,
|
||||||
|
y = go.transform.position.y,
|
||||||
|
z = go.transform.position.z,
|
||||||
|
},
|
||||||
|
localPosition = new
|
||||||
|
{
|
||||||
|
x = go.transform.localPosition.x,
|
||||||
|
y = go.transform.localPosition.y,
|
||||||
|
z = go.transform.localPosition.z,
|
||||||
|
},
|
||||||
|
rotation = new
|
||||||
|
{
|
||||||
|
x = go.transform.rotation.eulerAngles.x,
|
||||||
|
y = go.transform.rotation.eulerAngles.y,
|
||||||
|
z = go.transform.rotation.eulerAngles.z,
|
||||||
|
},
|
||||||
|
localRotation = new
|
||||||
|
{
|
||||||
|
x = go.transform.localRotation.eulerAngles.x,
|
||||||
|
y = go.transform.localRotation.eulerAngles.y,
|
||||||
|
z = go.transform.localRotation.eulerAngles.z,
|
||||||
|
},
|
||||||
|
scale = new
|
||||||
|
{
|
||||||
|
x = go.transform.localScale.x,
|
||||||
|
y = go.transform.localScale.y,
|
||||||
|
z = go.transform.localScale.z,
|
||||||
|
},
|
||||||
|
forward = new
|
||||||
|
{
|
||||||
|
x = go.transform.forward.x,
|
||||||
|
y = go.transform.forward.y,
|
||||||
|
z = go.transform.forward.z,
|
||||||
|
},
|
||||||
|
up = new
|
||||||
|
{
|
||||||
|
x = go.transform.up.x,
|
||||||
|
y = go.transform.up.y,
|
||||||
|
z = go.transform.up.z,
|
||||||
|
},
|
||||||
|
right = new
|
||||||
|
{
|
||||||
|
x = go.transform.right.x,
|
||||||
|
y = go.transform.right.y,
|
||||||
|
z = go.transform.right.z,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
parentInstanceID = go.transform.parent?.gameObject.GetInstanceID() ?? 0, // 0 if no parent
|
||||||
|
// Optionally include components, but can be large
|
||||||
|
// components = go.GetComponents<Component>().Select(c => GetComponentData(c)).ToList()
|
||||||
|
// Or just component names:
|
||||||
|
componentNames = go.GetComponents<Component>()
|
||||||
|
.Select(c => c.GetType().FullName)
|
||||||
|
.ToList(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Metadata Caching for Reflection ---
|
||||||
|
private class CachedMetadata
|
||||||
|
{
|
||||||
|
public readonly List<PropertyInfo> SerializableProperties;
|
||||||
|
public readonly List<FieldInfo> SerializableFields;
|
||||||
|
|
||||||
|
public CachedMetadata(List<PropertyInfo> properties, List<FieldInfo> fields)
|
||||||
|
{
|
||||||
|
SerializableProperties = properties;
|
||||||
|
SerializableFields = fields;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Key becomes Tuple<Type, bool>
|
||||||
|
private static readonly Dictionary<Tuple<Type, bool>, CachedMetadata> _metadataCache = new Dictionary<Tuple<Type, bool>, CachedMetadata>();
|
||||||
|
// --- End Metadata Caching ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a serializable representation of a Component, attempting to serialize
|
||||||
|
/// public properties and fields using reflection, with caching and control over non-public fields.
|
||||||
|
/// </summary>
|
||||||
|
// Add the flag parameter here
|
||||||
|
public static object GetComponentData(Component c, bool includeNonPublicSerializedFields = true)
|
||||||
|
{
|
||||||
|
// --- Add Early Logging ---
|
||||||
|
// Debug.Log($"[GetComponentData] Starting for component: {c?.GetType()?.FullName ?? "null"} (ID: {c?.GetInstanceID() ?? 0})");
|
||||||
|
// --- End Early Logging ---
|
||||||
|
|
||||||
|
if (c == null) return null;
|
||||||
|
Type componentType = c.GetType();
|
||||||
|
|
||||||
|
// --- Special handling for Transform to avoid reflection crashes and problematic properties ---
|
||||||
|
if (componentType == typeof(Transform))
|
||||||
|
{
|
||||||
|
Transform tr = c as Transform;
|
||||||
|
// Debug.Log($"[GetComponentData] Manually serializing Transform (ID: {tr.GetInstanceID()})");
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "typeName", componentType.FullName },
|
||||||
|
{ "instanceID", tr.GetInstanceID() },
|
||||||
|
// Manually extract known-safe properties. Avoid Quaternion 'rotation' and 'lossyScale'.
|
||||||
|
{ "position", CreateTokenFromValue(tr.position, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "localPosition", CreateTokenFromValue(tr.localPosition, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "eulerAngles", CreateTokenFromValue(tr.eulerAngles, typeof(Vector3))?.ToObject<object>() ?? new JObject() }, // Use Euler angles
|
||||||
|
{ "localEulerAngles", CreateTokenFromValue(tr.localEulerAngles, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "localScale", CreateTokenFromValue(tr.localScale, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "right", CreateTokenFromValue(tr.right, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "up", CreateTokenFromValue(tr.up, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "forward", CreateTokenFromValue(tr.forward, typeof(Vector3))?.ToObject<object>() ?? new JObject() },
|
||||||
|
{ "parentInstanceID", tr.parent?.gameObject.GetInstanceID() ?? 0 },
|
||||||
|
{ "rootInstanceID", tr.root?.gameObject.GetInstanceID() ?? 0 },
|
||||||
|
{ "childCount", tr.childCount },
|
||||||
|
// Include standard Object/Component properties
|
||||||
|
{ "name", tr.name },
|
||||||
|
{ "tag", tr.tag },
|
||||||
|
{ "gameObjectInstanceID", tr.gameObject?.GetInstanceID() ?? 0 }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// --- End Special handling for Transform ---
|
||||||
|
|
||||||
|
// --- Special handling for Camera to avoid matrix-related crashes ---
|
||||||
|
if (componentType == typeof(Camera))
|
||||||
|
{
|
||||||
|
Camera cam = c as Camera;
|
||||||
|
var cameraProperties = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// List of safe properties to serialize
|
||||||
|
var safeProperties = new Dictionary<string, Func<object>>
|
||||||
|
{
|
||||||
|
{ "nearClipPlane", () => cam.nearClipPlane },
|
||||||
|
{ "farClipPlane", () => cam.farClipPlane },
|
||||||
|
{ "fieldOfView", () => cam.fieldOfView },
|
||||||
|
{ "renderingPath", () => (int)cam.renderingPath },
|
||||||
|
{ "actualRenderingPath", () => (int)cam.actualRenderingPath },
|
||||||
|
{ "allowHDR", () => cam.allowHDR },
|
||||||
|
{ "allowMSAA", () => cam.allowMSAA },
|
||||||
|
{ "allowDynamicResolution", () => cam.allowDynamicResolution },
|
||||||
|
{ "forceIntoRenderTexture", () => cam.forceIntoRenderTexture },
|
||||||
|
{ "orthographicSize", () => cam.orthographicSize },
|
||||||
|
{ "orthographic", () => cam.orthographic },
|
||||||
|
{ "opaqueSortMode", () => (int)cam.opaqueSortMode },
|
||||||
|
{ "transparencySortMode", () => (int)cam.transparencySortMode },
|
||||||
|
{ "depth", () => cam.depth },
|
||||||
|
{ "aspect", () => cam.aspect },
|
||||||
|
{ "cullingMask", () => cam.cullingMask },
|
||||||
|
{ "eventMask", () => cam.eventMask },
|
||||||
|
{ "backgroundColor", () => cam.backgroundColor },
|
||||||
|
{ "clearFlags", () => (int)cam.clearFlags },
|
||||||
|
{ "stereoEnabled", () => cam.stereoEnabled },
|
||||||
|
{ "stereoSeparation", () => cam.stereoSeparation },
|
||||||
|
{ "stereoConvergence", () => cam.stereoConvergence },
|
||||||
|
{ "enabled", () => cam.enabled },
|
||||||
|
{ "name", () => cam.name },
|
||||||
|
{ "tag", () => cam.tag },
|
||||||
|
{ "gameObject", () => new { name = cam.gameObject.name, instanceID = cam.gameObject.GetInstanceID() } }
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var prop in safeProperties)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var value = prop.Value();
|
||||||
|
if (value != null)
|
||||||
|
{
|
||||||
|
AddSerializableValue(cameraProperties, prop.Key, value.GetType(), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
// Silently skip any property that fails
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "typeName", componentType.FullName },
|
||||||
|
{ "instanceID", cam.GetInstanceID() },
|
||||||
|
{ "properties", cameraProperties }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// --- End Special handling for Camera ---
|
||||||
|
|
||||||
|
var data = new Dictionary<string, object>
|
||||||
|
{
|
||||||
|
{ "typeName", componentType.FullName },
|
||||||
|
{ "instanceID", c.GetInstanceID() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Get Cached or Generate Metadata (using new cache key) ---
|
||||||
|
Tuple<Type, bool> cacheKey = new Tuple<Type, bool>(componentType, includeNonPublicSerializedFields);
|
||||||
|
if (!_metadataCache.TryGetValue(cacheKey, out CachedMetadata cachedData))
|
||||||
|
{
|
||||||
|
var propertiesToCache = new List<PropertyInfo>();
|
||||||
|
var fieldsToCache = new List<FieldInfo>();
|
||||||
|
|
||||||
|
// Traverse the hierarchy from the component type up to MonoBehaviour
|
||||||
|
Type currentType = componentType;
|
||||||
|
while (currentType != null && currentType != typeof(MonoBehaviour) && currentType != typeof(object))
|
||||||
|
{
|
||||||
|
// Get properties declared only at the current type level
|
||||||
|
BindingFlags propFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||||
|
foreach (var propInfo in currentType.GetProperties(propFlags))
|
||||||
|
{
|
||||||
|
// Basic filtering (readable, not indexer, not transform which is handled elsewhere)
|
||||||
|
if (!propInfo.CanRead || propInfo.GetIndexParameters().Length > 0 || propInfo.Name == "transform") continue;
|
||||||
|
// Add if not already added (handles overrides - keep the most derived version)
|
||||||
|
if (!propertiesToCache.Any(p => p.Name == propInfo.Name)) {
|
||||||
|
propertiesToCache.Add(propInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get fields declared only at the current type level (both public and non-public)
|
||||||
|
BindingFlags fieldFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||||
|
var declaredFields = currentType.GetFields(fieldFlags);
|
||||||
|
|
||||||
|
// Process the declared Fields for caching
|
||||||
|
foreach (var fieldInfo in declaredFields)
|
||||||
|
{
|
||||||
|
if (fieldInfo.Name.EndsWith("k__BackingField")) continue; // Skip backing fields
|
||||||
|
|
||||||
|
// Add if not already added (handles hiding - keep the most derived version)
|
||||||
|
if (fieldsToCache.Any(f => f.Name == fieldInfo.Name)) continue;
|
||||||
|
|
||||||
|
bool shouldInclude = false;
|
||||||
|
if (includeNonPublicSerializedFields)
|
||||||
|
{
|
||||||
|
// If TRUE, include Public OR NonPublic with [SerializeField]
|
||||||
|
shouldInclude = fieldInfo.IsPublic || (fieldInfo.IsPrivate && fieldInfo.IsDefined(typeof(SerializeField), inherit: false));
|
||||||
|
}
|
||||||
|
else // includeNonPublicSerializedFields is FALSE
|
||||||
|
{
|
||||||
|
// If FALSE, include ONLY if it is explicitly Public.
|
||||||
|
shouldInclude = fieldInfo.IsPublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldInclude)
|
||||||
|
{
|
||||||
|
fieldsToCache.Add(fieldInfo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move to the base type
|
||||||
|
currentType = currentType.BaseType;
|
||||||
|
}
|
||||||
|
// --- End Hierarchy Traversal ---
|
||||||
|
|
||||||
|
cachedData = new CachedMetadata(propertiesToCache, fieldsToCache);
|
||||||
|
_metadataCache[cacheKey] = cachedData; // Add to cache with combined key
|
||||||
|
}
|
||||||
|
// --- End Get Cached or Generate Metadata ---
|
||||||
|
|
||||||
|
// --- Use cached metadata ---
|
||||||
|
var serializablePropertiesOutput = new Dictionary<string, object>();
|
||||||
|
|
||||||
|
// --- Add Logging Before Property Loop ---
|
||||||
|
// Debug.Log($"[GetComponentData] Starting property loop for {componentType.Name}...");
|
||||||
|
// --- End Logging Before Property Loop ---
|
||||||
|
|
||||||
|
// Use cached properties
|
||||||
|
foreach (var propInfo in cachedData.SerializableProperties)
|
||||||
|
{
|
||||||
|
string propName = propInfo.Name;
|
||||||
|
|
||||||
|
// --- Skip known obsolete/problematic Component shortcut properties ---
|
||||||
|
bool skipProperty = false;
|
||||||
|
if (propName == "rigidbody" || propName == "rigidbody2D" || propName == "camera" ||
|
||||||
|
propName == "light" || propName == "animation" || propName == "constantForce" ||
|
||||||
|
propName == "renderer" || propName == "audio" || propName == "networkView" ||
|
||||||
|
propName == "collider" || propName == "collider2D" || propName == "hingeJoint" ||
|
||||||
|
propName == "particleSystem" ||
|
||||||
|
// Also skip potentially problematic Matrix properties prone to cycles/errors
|
||||||
|
propName == "worldToLocalMatrix" || propName == "localToWorldMatrix")
|
||||||
|
{
|
||||||
|
// Debug.Log($"[GetComponentData] Explicitly skipping generic property: {propName}"); // Optional log
|
||||||
|
skipProperty = true;
|
||||||
|
}
|
||||||
|
// --- End Skip Generic Properties ---
|
||||||
|
|
||||||
|
// --- Skip specific potentially problematic Camera properties ---
|
||||||
|
if (componentType == typeof(Camera) &&
|
||||||
|
(propName == "pixelRect" ||
|
||||||
|
propName == "rect" ||
|
||||||
|
propName == "cullingMatrix" ||
|
||||||
|
propName == "useOcclusionCulling" ||
|
||||||
|
propName == "worldToCameraMatrix" ||
|
||||||
|
propName == "projectionMatrix" ||
|
||||||
|
propName == "nonJitteredProjectionMatrix" ||
|
||||||
|
propName == "previousViewProjectionMatrix" ||
|
||||||
|
propName == "cameraToWorldMatrix"))
|
||||||
|
{
|
||||||
|
// Debug.Log($"[GetComponentData] Explicitly skipping Camera property: {propName}");
|
||||||
|
skipProperty = true;
|
||||||
|
}
|
||||||
|
// --- End Skip Camera Properties ---
|
||||||
|
|
||||||
|
// --- Skip specific potentially problematic Transform properties ---
|
||||||
|
if (componentType == typeof(Transform) &&
|
||||||
|
(propName == "lossyScale" ||
|
||||||
|
propName == "rotation" ||
|
||||||
|
propName == "worldToLocalMatrix" ||
|
||||||
|
propName == "localToWorldMatrix"))
|
||||||
|
{
|
||||||
|
// Debug.Log($"[GetComponentData] Explicitly skipping Transform property: {propName}");
|
||||||
|
skipProperty = true;
|
||||||
|
}
|
||||||
|
// --- End Skip Transform Properties ---
|
||||||
|
|
||||||
|
// Skip if flagged
|
||||||
|
if (skipProperty)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// --- Add detailed logging ---
|
||||||
|
// Debug.Log($"[GetComponentData] Accessing: {componentType.Name}.{propName}");
|
||||||
|
// --- End detailed logging ---
|
||||||
|
object value = propInfo.GetValue(c);
|
||||||
|
Type propType = propInfo.PropertyType;
|
||||||
|
AddSerializableValue(serializablePropertiesOutput, propName, propType, value);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Debug.LogWarning($"Could not read property {propName} on {componentType.Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Add Logging Before Field Loop ---
|
||||||
|
// Debug.Log($"[GetComponentData] Starting field loop for {componentType.Name}...");
|
||||||
|
// --- End Logging Before Field Loop ---
|
||||||
|
|
||||||
|
// Use cached fields
|
||||||
|
foreach (var fieldInfo in cachedData.SerializableFields)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// --- Add detailed logging for fields ---
|
||||||
|
// Debug.Log($"[GetComponentData] Accessing Field: {componentType.Name}.{fieldInfo.Name}");
|
||||||
|
// --- End detailed logging for fields ---
|
||||||
|
object value = fieldInfo.GetValue(c);
|
||||||
|
string fieldName = fieldInfo.Name;
|
||||||
|
Type fieldType = fieldInfo.FieldType;
|
||||||
|
AddSerializableValue(serializablePropertiesOutput, fieldName, fieldType, value);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
// Debug.LogWarning($"Could not read field {fieldInfo.Name} on {componentType.Name}: {ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// --- End Use cached metadata ---
|
||||||
|
|
||||||
|
if (serializablePropertiesOutput.Count > 0)
|
||||||
|
{
|
||||||
|
data["properties"] = serializablePropertiesOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to decide how to serialize different types
|
||||||
|
private static void AddSerializableValue(Dictionary<string, object> dict, string name, Type type, object value)
|
||||||
|
{
|
||||||
|
// Simplified: Directly use CreateTokenFromValue which uses the serializer
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
dict[name] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use the helper that employs our custom serializer settings
|
||||||
|
JToken token = CreateTokenFromValue(value, type);
|
||||||
|
if (token != null) // Check if serialization succeeded in the helper
|
||||||
|
{
|
||||||
|
// Convert JToken back to a basic object structure for the dictionary
|
||||||
|
dict[name] = ConvertJTokenToPlainObject(token);
|
||||||
|
}
|
||||||
|
// If token is null, it means serialization failed and a warning was logged.
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
// Catch potential errors during JToken conversion or addition to dictionary
|
||||||
|
Debug.LogWarning($"[AddSerializableValue] Error processing value for '{name}' (Type: {type.FullName}): {e.Message}. Skipping.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to convert JToken back to basic object structure
|
||||||
|
private static object ConvertJTokenToPlainObject(JToken token)
|
||||||
|
{
|
||||||
|
if (token == null) return null;
|
||||||
|
|
||||||
|
switch (token.Type)
|
||||||
|
{
|
||||||
|
case JTokenType.Object:
|
||||||
|
var objDict = new Dictionary<string, object>();
|
||||||
|
foreach (var prop in ((JObject)token).Properties())
|
||||||
|
{
|
||||||
|
objDict[prop.Name] = ConvertJTokenToPlainObject(prop.Value);
|
||||||
|
}
|
||||||
|
return objDict;
|
||||||
|
|
||||||
|
case JTokenType.Array:
|
||||||
|
var list = new List<object>();
|
||||||
|
foreach (var item in (JArray)token)
|
||||||
|
{
|
||||||
|
list.Add(ConvertJTokenToPlainObject(item));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
|
||||||
|
case JTokenType.Integer:
|
||||||
|
return token.ToObject<long>(); // Use long for safety
|
||||||
|
case JTokenType.Float:
|
||||||
|
return token.ToObject<double>(); // Use double for safety
|
||||||
|
case JTokenType.String:
|
||||||
|
return token.ToObject<string>();
|
||||||
|
case JTokenType.Boolean:
|
||||||
|
return token.ToObject<bool>();
|
||||||
|
case JTokenType.Date:
|
||||||
|
return token.ToObject<DateTime>();
|
||||||
|
case JTokenType.Guid:
|
||||||
|
return token.ToObject<Guid>();
|
||||||
|
case JTokenType.Uri:
|
||||||
|
return token.ToObject<Uri>();
|
||||||
|
case JTokenType.TimeSpan:
|
||||||
|
return token.ToObject<TimeSpan>();
|
||||||
|
case JTokenType.Bytes:
|
||||||
|
return token.ToObject<byte[]>();
|
||||||
|
case JTokenType.Null:
|
||||||
|
return null;
|
||||||
|
case JTokenType.Undefined:
|
||||||
|
return null; // Treat undefined as null
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Fallback for simple value types not explicitly listed
|
||||||
|
if (token is JValue jValue && jValue.Value != null)
|
||||||
|
{
|
||||||
|
return jValue.Value;
|
||||||
|
}
|
||||||
|
// Debug.LogWarning($"Unsupported JTokenType encountered: {token.Type}. Returning null.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Define custom JsonSerializerSettings for OUTPUT ---
|
||||||
|
private static readonly JsonSerializerSettings _outputSerializerSettings = new JsonSerializerSettings
|
||||||
|
{
|
||||||
|
Converters = new List<JsonConverter>
|
||||||
|
{
|
||||||
|
new Vector3Converter(),
|
||||||
|
new Vector2Converter(),
|
||||||
|
new QuaternionConverter(),
|
||||||
|
new ColorConverter(),
|
||||||
|
new RectConverter(),
|
||||||
|
new BoundsConverter(),
|
||||||
|
new UnityEngineObjectConverter() // Handles serialization of references
|
||||||
|
},
|
||||||
|
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
|
||||||
|
// ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() } // Example if needed
|
||||||
|
};
|
||||||
|
private static readonly JsonSerializer _outputSerializer = JsonSerializer.Create(_outputSerializerSettings);
|
||||||
|
// --- End Define custom JsonSerializerSettings ---
|
||||||
|
|
||||||
|
// Helper to create JToken using the output serializer
|
||||||
|
private static JToken CreateTokenFromValue(object value, Type type)
|
||||||
|
{
|
||||||
|
if (value == null) return JValue.CreateNull();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Use the pre-configured OUTPUT serializer instance
|
||||||
|
return JToken.FromObject(value, _outputSerializer);
|
||||||
|
}
|
||||||
|
catch (JsonSerializationException e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[GameObjectSerializer] Newtonsoft.Json Error serializing value of type {type.FullName}: {e.Message}. Skipping property/field.");
|
||||||
|
return null; // Indicate serialization failure
|
||||||
|
}
|
||||||
|
catch (Exception e) // Catch other unexpected errors
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[GameObjectSerializer] Unexpected error serializing value of type {type.FullName}: {e}. Skipping property/field.");
|
||||||
|
return null; // Indicate serialization failure
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 64b8ff807bc9a401c82015cbafccffac
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"name": "UnityMcpBridge.Editor",
|
||||||
|
"rootNamespace": "UnityMcpBridge.Editor",
|
||||||
|
"references": [
|
||||||
|
"UnityMcpBridge.Runtime",
|
||||||
|
"GUID:560b04d1a97f54a46a2660c3cc343a6f"
|
||||||
|
],
|
||||||
|
"includePlatforms": [
|
||||||
|
"Editor"
|
||||||
|
],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 04b0581466993404a8fae14802c2a5a6
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -267,6 +267,7 @@ namespace UnityMcpBridge.Editor
|
||||||
|
|
||||||
// Normal JSON command processing
|
// Normal JSON command processing
|
||||||
Command command = JsonConvert.DeserializeObject<Command>(commandText);
|
Command command = JsonConvert.DeserializeObject<Command>(commandText);
|
||||||
|
|
||||||
if (command == null)
|
if (command == null)
|
||||||
{
|
{
|
||||||
var nullCommandResponse = new
|
var nullCommandResponse = new
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b5cc10fd969474b3680332e542416860
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c7e33d6224fe6473f9bc69fe6d40e508
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -0,0 +1,266 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
using UnityEditor; // Required for AssetDatabase and EditorUtility
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace UnityMcpBridge.Runtime.Serialization
|
||||||
|
{
|
||||||
|
public class Vector3Converter : JsonConverter<Vector3>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Vector3 value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("x");
|
||||||
|
writer.WriteValue(value.x);
|
||||||
|
writer.WritePropertyName("y");
|
||||||
|
writer.WriteValue(value.y);
|
||||||
|
writer.WritePropertyName("z");
|
||||||
|
writer.WriteValue(value.z);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector3 ReadJson(JsonReader reader, Type objectType, Vector3 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
return new Vector3(
|
||||||
|
(float)jo["x"],
|
||||||
|
(float)jo["y"],
|
||||||
|
(float)jo["z"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Vector2Converter : JsonConverter<Vector2>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Vector2 value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("x");
|
||||||
|
writer.WriteValue(value.x);
|
||||||
|
writer.WritePropertyName("y");
|
||||||
|
writer.WriteValue(value.y);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Vector2 ReadJson(JsonReader reader, Type objectType, Vector2 existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
return new Vector2(
|
||||||
|
(float)jo["x"],
|
||||||
|
(float)jo["y"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class QuaternionConverter : JsonConverter<Quaternion>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Quaternion value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("x");
|
||||||
|
writer.WriteValue(value.x);
|
||||||
|
writer.WritePropertyName("y");
|
||||||
|
writer.WriteValue(value.y);
|
||||||
|
writer.WritePropertyName("z");
|
||||||
|
writer.WriteValue(value.z);
|
||||||
|
writer.WritePropertyName("w");
|
||||||
|
writer.WriteValue(value.w);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Quaternion ReadJson(JsonReader reader, Type objectType, Quaternion existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
return new Quaternion(
|
||||||
|
(float)jo["x"],
|
||||||
|
(float)jo["y"],
|
||||||
|
(float)jo["z"],
|
||||||
|
(float)jo["w"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ColorConverter : JsonConverter<Color>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Color value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("r");
|
||||||
|
writer.WriteValue(value.r);
|
||||||
|
writer.WritePropertyName("g");
|
||||||
|
writer.WriteValue(value.g);
|
||||||
|
writer.WritePropertyName("b");
|
||||||
|
writer.WriteValue(value.b);
|
||||||
|
writer.WritePropertyName("a");
|
||||||
|
writer.WriteValue(value.a);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Color ReadJson(JsonReader reader, Type objectType, Color existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
return new Color(
|
||||||
|
(float)jo["r"],
|
||||||
|
(float)jo["g"],
|
||||||
|
(float)jo["b"],
|
||||||
|
(float)jo["a"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RectConverter : JsonConverter<Rect>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Rect value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("x");
|
||||||
|
writer.WriteValue(value.x);
|
||||||
|
writer.WritePropertyName("y");
|
||||||
|
writer.WriteValue(value.y);
|
||||||
|
writer.WritePropertyName("width");
|
||||||
|
writer.WriteValue(value.width);
|
||||||
|
writer.WritePropertyName("height");
|
||||||
|
writer.WriteValue(value.height);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Rect ReadJson(JsonReader reader, Type objectType, Rect existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
return new Rect(
|
||||||
|
(float)jo["x"],
|
||||||
|
(float)jo["y"],
|
||||||
|
(float)jo["width"],
|
||||||
|
(float)jo["height"]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BoundsConverter : JsonConverter<Bounds>
|
||||||
|
{
|
||||||
|
public override void WriteJson(JsonWriter writer, Bounds value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("center");
|
||||||
|
serializer.Serialize(writer, value.center); // Use serializer to handle nested Vector3
|
||||||
|
writer.WritePropertyName("size");
|
||||||
|
serializer.Serialize(writer, value.size); // Use serializer to handle nested Vector3
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Bounds ReadJson(JsonReader reader, Type objectType, Bounds existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
Vector3 center = jo["center"].ToObject<Vector3>(serializer); // Use serializer to handle nested Vector3
|
||||||
|
Vector3 size = jo["size"].ToObject<Vector3>(serializer); // Use serializer to handle nested Vector3
|
||||||
|
return new Bounds(center, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Converter for UnityEngine.Object references (GameObjects, Components, Materials, Textures, etc.)
|
||||||
|
public class UnityEngineObjectConverter : JsonConverter<UnityEngine.Object>
|
||||||
|
{
|
||||||
|
public override bool CanRead => true; // We need to implement ReadJson
|
||||||
|
public override bool CanWrite => true;
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, UnityEngine.Object value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
writer.WriteNull();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR // AssetDatabase and EditorUtility are Editor-only
|
||||||
|
if (UnityEditor.AssetDatabase.Contains(value))
|
||||||
|
{
|
||||||
|
// It's an asset (Material, Texture, Prefab, etc.)
|
||||||
|
string path = UnityEditor.AssetDatabase.GetAssetPath(value);
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
writer.WriteValue(path);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Asset exists but path couldn't be found? Write minimal info.
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("name");
|
||||||
|
writer.WriteValue(value.name);
|
||||||
|
writer.WritePropertyName("instanceID");
|
||||||
|
writer.WriteValue(value.GetInstanceID());
|
||||||
|
writer.WritePropertyName("isAssetWithoutPath");
|
||||||
|
writer.WriteValue(true);
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// It's a scene object (GameObject, Component, etc.)
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("name");
|
||||||
|
writer.WriteValue(value.name);
|
||||||
|
writer.WritePropertyName("instanceID");
|
||||||
|
writer.WriteValue(value.GetInstanceID());
|
||||||
|
writer.WriteEndObject();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Runtime fallback: Write basic info without AssetDatabase
|
||||||
|
writer.WriteStartObject();
|
||||||
|
writer.WritePropertyName("name");
|
||||||
|
writer.WriteValue(value.name);
|
||||||
|
writer.WritePropertyName("instanceID");
|
||||||
|
writer.WriteValue(value.GetInstanceID());
|
||||||
|
writer.WritePropertyName("warning");
|
||||||
|
writer.WriteValue("UnityEngineObjectConverter running in non-Editor mode, asset path unavailable.");
|
||||||
|
writer.WriteEndObject();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public override UnityEngine.Object ReadJson(JsonReader reader, Type objectType, UnityEngine.Object existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.TokenType == JsonToken.Null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
if (reader.TokenType == JsonToken.String)
|
||||||
|
{
|
||||||
|
// Assume it's an asset path
|
||||||
|
string path = reader.Value.ToString();
|
||||||
|
return UnityEditor.AssetDatabase.LoadAssetAtPath(path, objectType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reader.TokenType == JsonToken.StartObject)
|
||||||
|
{
|
||||||
|
JObject jo = JObject.Load(reader);
|
||||||
|
if (jo.TryGetValue("instanceID", out JToken idToken) && idToken.Type == JTokenType.Integer)
|
||||||
|
{
|
||||||
|
int instanceId = idToken.ToObject<int>();
|
||||||
|
UnityEngine.Object obj = UnityEditor.EditorUtility.InstanceIDToObject(instanceId);
|
||||||
|
if (obj != null && objectType.IsAssignableFrom(obj.GetType()))
|
||||||
|
{
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Could potentially try finding by name as a fallback if ID lookup fails/isn't present
|
||||||
|
// but that's less reliable.
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Runtime deserialization is tricky without AssetDatabase/EditorUtility
|
||||||
|
// Maybe log a warning and return null or existingValue?
|
||||||
|
Debug.LogWarning("UnityEngineObjectConverter cannot deserialize complex objects in non-Editor mode.");
|
||||||
|
// Skip the token to avoid breaking the reader
|
||||||
|
if (reader.TokenType == JsonToken.StartObject) JObject.Load(reader);
|
||||||
|
else if (reader.TokenType == JsonToken.String) reader.ReadAsString();
|
||||||
|
// Return null or existing value, depending on desired behavior
|
||||||
|
return existingValue;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
throw new JsonSerializationException($"Unexpected token type '{reader.TokenType}' when deserializing UnityEngine.Object");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e65311c160f0d41d4a1b45a3dba8dd5a
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "UnityMcpBridge.Runtime",
|
||||||
|
"rootNamespace": "UnityMcpBridge.Runtime",
|
||||||
|
"references": [
|
||||||
|
"GUID:560b04d1a97f54a46a2660c3cc343a6f"
|
||||||
|
],
|
||||||
|
"includePlatforms": [],
|
||||||
|
"excludePlatforms": [],
|
||||||
|
"allowUnsafeCode": false,
|
||||||
|
"overrideReferences": false,
|
||||||
|
"precompiledReferences": [],
|
||||||
|
"autoReferenced": true,
|
||||||
|
"defineConstraints": [],
|
||||||
|
"versionDefines": [],
|
||||||
|
"noEngineReferences": false
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 7d76fa93cbc5144028727fd2dbac5655
|
||||||
|
AssemblyDefinitionImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -35,11 +35,12 @@ def register_manage_gameobject_tools(mcp: FastMCP):
|
||||||
search_inactive: bool = False,
|
search_inactive: bool = False,
|
||||||
# -- Component Management Arguments --
|
# -- Component Management Arguments --
|
||||||
component_name: str = None,
|
component_name: str = None,
|
||||||
|
includeNonPublicSerialized: bool = None, # Controls serialization of private [SerializeField] fields
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""Manages GameObjects: create, modify, delete, find, and component operations.
|
"""Manages GameObjects: create, modify, delete, find, and component operations.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
action: Operation (e.g., 'create', 'modify', 'find', 'add_component', 'remove_component', 'set_component_property').
|
action: Operation (e.g., 'create', 'modify', 'find', 'add_component', 'remove_component', 'set_component_property', 'get_components').
|
||||||
target: GameObject identifier (name or path string) for modify/delete/component actions.
|
target: GameObject identifier (name or path string) for modify/delete/component actions.
|
||||||
search_method: How to find objects ('by_name', 'by_id', 'by_path', etc.). Used with 'find' and some 'target' lookups.
|
search_method: How to find objects ('by_name', 'by_id', 'by_path', etc.). Used with 'find' and some 'target' lookups.
|
||||||
name: GameObject name - used for both 'create' (initial name) and 'modify' (rename).
|
name: GameObject name - used for both 'create' (initial name) and 'modify' (rename).
|
||||||
|
|
@ -59,9 +60,18 @@ def register_manage_gameobject_tools(mcp: FastMCP):
|
||||||
Action-specific arguments (e.g., position, rotation, scale for create/modify;
|
Action-specific arguments (e.g., position, rotation, scale for create/modify;
|
||||||
component_name for component actions;
|
component_name for component actions;
|
||||||
search_term, find_all for 'find').
|
search_term, find_all for 'find').
|
||||||
|
includeNonPublicSerialized: If True, includes private fields marked [SerializeField] in component data.
|
||||||
|
|
||||||
|
Action-specific details:
|
||||||
|
- For 'get_components':
|
||||||
|
Required: target, search_method
|
||||||
|
Optional: includeNonPublicSerialized (defaults to True)
|
||||||
|
Returns all components on the target GameObject with their serialized data.
|
||||||
|
The search_method parameter determines how to find the target ('by_name', 'by_id', 'by_path').
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Dictionary with operation results ('success', 'message', 'data').
|
Dictionary with operation results ('success', 'message', 'data').
|
||||||
|
For 'get_components', the 'data' field contains a dictionary of component names and their serialized properties.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# --- Early check for attempting to modify a prefab asset ---
|
# --- Early check for attempting to modify a prefab asset ---
|
||||||
|
|
@ -91,7 +101,8 @@ def register_manage_gameobject_tools(mcp: FastMCP):
|
||||||
"findAll": find_all,
|
"findAll": find_all,
|
||||||
"searchInChildren": search_in_children,
|
"searchInChildren": search_in_children,
|
||||||
"searchInactive": search_inactive,
|
"searchInactive": search_inactive,
|
||||||
"componentName": component_name
|
"componentName": component_name,
|
||||||
|
"includeNonPublicSerialized": includeNonPublicSerialized
|
||||||
}
|
}
|
||||||
params = {k: v for k, v in params.items() if v is not None}
|
params = {k: v for k, v in params.items() if v is not None}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -321,8 +321,8 @@ wheels = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unity-mcp"
|
name = "unitymcpserver"
|
||||||
version = "1.0.1"
|
version = "2.0.0"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "httpx" },
|
{ name = "httpx" },
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue