using System;
using UnityEngine;
using UnityEditor;
namespace LocalMcp.UnityServer
{
/// <summary>
/// Provides SerializedProperty write capabilities for private/protected/SerializeField access
/// </summary>
public static class SerializedPropertyWriter
{
/// <summary>
/// Sets a serialized property value (supports private/protected fields with SerializeField)
/// </summary>
public static string SetSerializedProperty(string paramsJson, object id)
{
try
{
var parameters = JsonUtility.FromJson<SetSerializedPropertyParams>(paramsJson);
if (string.IsNullOrEmpty(parameters.gameObjectPath))
{
return JsonRpcResponseHelper.InvalidParams("gameObjectPath is required", id);
}
if (string.IsNullOrEmpty(parameters.componentType))
{
return JsonRpcResponseHelper.InvalidParams("componentType is required", id);
}
if (string.IsNullOrEmpty(parameters.propertyPath))
{
return JsonRpcResponseHelper.InvalidParams("propertyPath is required", id);
}
GameObject go = GameObject.Find(parameters.gameObjectPath);
if (go == null)
{
return JsonRpcResponseHelper.ErrorMessage($"GameObject '{parameters.gameObjectPath}' not found", id);
}
Component component = go.GetComponent(parameters.componentType);
if (component == null)
{
return JsonRpcResponseHelper.ErrorMessage($"Component '{parameters.componentType}' not found on GameObject", id);
}
// Create SerializedObject
SerializedObject serializedObject = new SerializedObject(component);
SerializedProperty property = serializedObject.FindProperty(parameters.propertyPath);
if (property == null)
{
return JsonRpcResponseHelper.ErrorMessage($"Property '{parameters.propertyPath}' not found", id);
}
// Set value based on property type
bool success = SetPropertyValue(property, parameters.value, parameters.valueType);
if (!success)
{
return JsonRpcResponseHelper.ErrorMessage($"Failed to set property value. Property type: {property.propertyType}", id);
}
// Apply changes
serializedObject.ApplyModifiedProperties();
var result = new SetSerializedPropertyResult
{
success = true,
gameObjectPath = parameters.gameObjectPath,
componentType = parameters.componentType,
propertyPath = parameters.propertyPath,
propertyType = property.propertyType.ToString()
};
var response = JsonRpcResponse.Success(result, id);
return response.ToJson();
}
catch (Exception e)
{
Debug.LogError($"[MCP] SetSerializedProperty error: {e.Message}\n{e.StackTrace}");
return JsonRpcResponseHelper.ErrorMessage($"Failed to set serialized property: {e.Message}", id);
}
}
private static bool SetPropertyValue(SerializedProperty property, string value, string valueType)
{
try
{
switch (property.propertyType)
{
case SerializedPropertyType.Integer:
property.intValue = Convert.ToInt32(value);
return true;
case SerializedPropertyType.Boolean:
property.boolValue = Convert.ToBoolean(value);
return true;
case SerializedPropertyType.Float:
property.floatValue = Convert.ToSingle(value);
return true;
case SerializedPropertyType.String:
property.stringValue = value;
return true;
case SerializedPropertyType.Color:
var colorData = JsonUtility.FromJson<ColorData>(value);
property.colorValue = new Color(colorData.r, colorData.g, colorData.b, colorData.a);
return true;
case SerializedPropertyType.Vector2:
var vec2Data = JsonUtility.FromJson<Vector2Data>(value);
property.vector2Value = new Vector2(vec2Data.x, vec2Data.y);
return true;
case SerializedPropertyType.Vector3:
var vec3Data = JsonUtility.FromJson<Vector3Data>(value);
property.vector3Value = new Vector3(vec3Data.x, vec3Data.y, vec3Data.z);
return true;
case SerializedPropertyType.Vector4:
var vec4Data = JsonUtility.FromJson<Vector4Data>(value);
property.vector4Value = new Vector4(vec4Data.x, vec4Data.y, vec4Data.z, vec4Data.w);
return true;
case SerializedPropertyType.Enum:
property.enumValueIndex = Convert.ToInt32(value);
return true;
case SerializedPropertyType.ObjectReference:
// For object references, value should be asset path
if (!string.IsNullOrEmpty(value))
{
var obj = AssetDatabase.LoadAssetAtPath<UnityEngine.Object>(value);
if (obj != null)
{
property.objectReferenceValue = obj;
return true;
}
}
else
{
// Set to null
property.objectReferenceValue = null;
return true;
}
return false;
default:
Debug.LogWarning($"[MCP] Unsupported property type: {property.propertyType}");
return false;
}
}
catch (Exception e)
{
Debug.LogError($"[MCP] SetPropertyValue error: {e.Message}");
return false;
}
}
}
#region Data Structures
[Serializable]
public class SetSerializedPropertyParams
{
public string gameObjectPath;
public string componentType;
public string propertyPath; // e.g., "m_Speed", "settings.maxValue"
public string value; // String representation of the value
public string valueType; // Optional: "int", "float", "bool", "string", "vector3", etc.
}
[Serializable]
public class SetSerializedPropertyResult
{
public bool success;
public string gameObjectPath;
public string componentType;
public string propertyPath;
public string propertyType;
}
[Serializable]
public class ColorData
{
public float r;
public float g;
public float b;
public float a;
}
// Note: Vector2Data, Vector3Data, Vector4Data are defined in SceneSetupCommand.cs
#endregion
}