Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class AssetObjectRefConverter : JsonConverter<AssetObjectRef>
assetObjectRef.AssetGuid = reader.GetString();
break;
default:
throw new JsonException($"Unexpected property name: {propertyName}. "
throw new JsonException($"[AssetObjectRefConverter] Unexpected property name: {propertyName}. "
+ $"Expected {AssetObjectRef.AssetObjectRefProperty.All.JoinEnclose()}.");
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ public class GameObjectRefConverter : JsonConverter<GameObjectRef>
case GameObjectRef.GameObjectRefProperty.Name:
result.Name = reader.GetString();
break;
case AssetObjectRef.AssetObjectRefProperty.AssetPath:
result.AssetPath = reader.GetString();
break;
case AssetObjectRef.AssetObjectRefProperty.AssetGuid:
result.AssetGuid = reader.GetString();
break;
default:
throw new JsonException($"Unexpected property name: {propertyName}. "
+ $"Expected {GameObjectRef.GameObjectRefProperty.All.JoinEnclose()}.");
Expand Down Expand Up @@ -83,6 +89,12 @@ public override void Write(Utf8JsonWriter writer, GameObjectRef value, JsonSeria
if (!string.IsNullOrEmpty(value.Name))
writer.WriteString(GameObjectRef.GameObjectRefProperty.Name, value.Name);

if (!string.IsNullOrEmpty(value.AssetPath))
writer.WriteString(AssetObjectRef.AssetObjectRefProperty.AssetPath, value.AssetPath);

if (!string.IsNullOrEmpty(value.AssetGuid))
writer.WriteString(AssetObjectRef.AssetObjectRefProperty.AssetGuid, value.AssetGuid);

writer.WriteEndObject();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,31 @@ protected override bool SetValue(
{
var padding = StringUtils.GetPadding(depth);

if (logger?.IsEnabled(LogLevel.Warning) == true)
logger.LogWarning($"{padding}Cannot set value for '{type.GetTypeShortName()}'. This type is not supported for setting values. Maybe did you want to set a field or a property? If so, set the value in the '{nameof(SerializedMember.fields)}' or '{nameof(SerializedMember.props)}' property instead.");
if (logger?.IsEnabled(LogLevel.Trace) == true)
logger.LogTrace($"{padding}Set value type='{type.GetTypeName(pretty: true)}'. Convertor='{GetType().GetTypeShortName()}'.");

if (stringBuilder != null)
stringBuilder.AppendLine($"{padding}[Warning] Cannot set value for '{type.GetTypeName(pretty: false)}'. This type is not supported for setting values. Maybe did you want to set a field or a property? If so, set the value in the '{nameof(SerializedMember.fields)}' or '{nameof(SerializedMember.props)}' property instead.");
try
{
obj = value
.ToGameObjectRef(
reflector: reflector,
suppressException: false,
depth: depth,
stringBuilder: stringBuilder,
logger: logger)
.FindGameObject();
return true;
}
catch (Exception ex)
{
if (logger?.IsEnabled(LogLevel.Error) == true)
logger.LogError(ex, $"{padding}[Error] Failed to deserialize value for type '{type.GetTypeName(pretty: false)}'. Convertor: {GetType().GetTypeShortName()}. Exception: {ex.Message}");

if (stringBuilder != null)
stringBuilder.AppendLine($"{padding}[Error] Failed to set value for type '{type.GetTypeName(pretty: false)}'. Convertor: {GetType().GetTypeShortName()}. Exception: {ex.Message}");

return false;
return false;
}
}

protected override bool TryPopulateField(
Expand All @@ -167,6 +185,9 @@ protected override bool TryPopulateField(
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
ILogger? logger = null)
{
if (logger?.IsEnabled(LogLevel.Information) == true)
logger.LogInformation($"[UnityEngine_GameObject_ReflectionConvertor] TryPopulateField called for obj type: {obj?.GetType().FullName}, field: {fieldValue.name}");

var padding = StringUtils.GetPadding(depth);
var go = obj as UnityEngine.GameObject;
if (go == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using UnityEngine;

namespace com.IvanMurzak.Unity.MCP.TestFiles
{
public class DataPopulationTestScript : MonoBehaviour
{
public Material materialField;
public GameObject gameObjectField;
public Texture2D textureField;
public Sprite spriteField;
public ScriptableObject scriptableObjectField;
public GameObject prefabField;

public Material[] materialArray;
public GameObject[] gameObjectArray;

public int intField;
public string stringField;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using UnityEngine;

namespace com.IvanMurzak.Unity.MCP.TestFiles
{
[CreateAssetMenu(fileName = "DataPopulationTestScriptableObject", menuName = "Tests/DataPopulationTestScriptableObject")]
public class DataPopulationTestScriptableObject : ScriptableObject
{
public int value;
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#nullable enable
using System.Linq;
using com.IvanMurzak.ReflectorNet.Utils;
using com.IvanMurzak.Unity.MCP.Runtime.Data;
using NUnit.Framework;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#nullable enable
using System.Linq;
using com.IvanMurzak.ReflectorNet.Utils;
using com.IvanMurzak.Unity.MCP.Runtime.Data;
using NUnit.Framework;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

#nullable enable
using System.Linq;
using com.IvanMurzak.ReflectorNet.Utils;
using com.IvanMurzak.Unity.MCP.Runtime.Data;
using NUnit.Framework;

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
#nullable enable
using System.Collections;
using System.Collections.Generic;
using com.IvanMurzak.ReflectorNet.Model;
using com.IvanMurzak.Unity.MCP.Editor.API;
using com.IvanMurzak.Unity.MCP.Editor.Tests.Utils;
using com.IvanMurzak.Unity.MCP.Runtime.Data;
using com.IvanMurzak.Unity.MCP.TestFiles;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;

namespace com.IvanMurzak.Unity.MCP.Editor.Tests
{
public class DataPopulationTests
{
[UnityTest]
public IEnumerator Populate_All_Types_Test()
{
// Executors for creating assets
var materialEx = new CreateMaterialExecutor("TestMaterial.mat", "Standard", "Assets", "Unity-MCP-Test", "DataPopulation");
var textureEx = new CreateTextureExecutor("TestTexture.png", "Assets", "Unity-MCP-Test", "DataPopulation");
var soEx = new CreateScriptableObjectExecutor<DataPopulationTestScriptableObject>("TestSO.asset", "Assets", "Unity-MCP-Test", "DataPopulation");

var prefabSourceGoEx = new CreateGameObjectExecutor("PrefabSource");
var prefabEx = new CreatePrefabExecutor("TestPrefab.prefab", null, "Assets", "Unity-MCP-Test", "DataPopulation");

// Target GameObject
var targetGoName = "TargetGO";
var targetGoRef = new GameObjectRef() { Name = targetGoName };
var targetGoEx = new CreateGameObjectExecutor(targetGoName);
var addCompEx = new AddComponentExecutor<DataPopulationTestScript>(targetGoRef);

// Validation Executor
var validateEx = new LazyNodeExecutor();
validateEx.SetAction<object?>((input) =>
{
var comp = addCompEx.Component;
Assert.IsNotNull(comp, "Component should exist");

Assert.AreEqual(42, comp!.intField, "intField not populated");
Assert.AreEqual("Hello World", comp.stringField, "stringField not populated");

Assert.IsNotNull(comp.materialField, "Material should be populated");
Assert.AreEqual(materialEx.Asset!.name, comp.materialField.name);

Assert.IsNotNull(comp.gameObjectField, "GameObject should be populated");
Assert.AreEqual(targetGoEx.GameObject!.name, comp.gameObjectField.name);

Assert.IsNotNull(comp.textureField, "Texture should be populated");
Assert.AreEqual(textureEx.Asset!.name, comp.textureField.name);

Assert.IsNotNull(comp.scriptableObjectField, "SO should be populated");
Assert.AreEqual(soEx.Asset!.name, comp.scriptableObjectField.name);

Assert.IsNotNull(comp.prefabField, "Prefab should be populated");
Assert.AreEqual(prefabEx.Asset!.name, comp.prefabField.name);

Assert.IsNotNull(comp.materialArray, "Material array should be populated");
Assert.AreEqual(2, comp!.materialArray!.Length);
Assert.AreEqual(materialEx.Asset.name, comp.materialArray[0].name);

Assert.IsNotNull(comp.gameObjectArray, "GameObject array should be populated");
Assert.AreEqual(2, comp.gameObjectArray!.Length);
});

// Chain creation
var modifyEx = new DynamicCallToolExecutor(
typeof(Tool_GameObject).GetMethod(nameof(Tool_GameObject.Modify)),
() =>
{
var reflector = McpPlugin.McpPlugin.Instance!.McpManager.Reflector;

var matRef = new AssetObjectRef() { AssetPath = materialEx.AssetPath };
var texRef = new AssetObjectRef() { AssetPath = textureEx.AssetPath };
var soRef = new AssetObjectRef() { AssetPath = soEx.AssetPath };
var prefabRef = new AssetObjectRef() { AssetPath = prefabEx.AssetPath };
var goRef = new ObjectRef(targetGoEx.GameObject!.GetInstanceID());

var goModification = SerializedMember.FromValue(
reflector: reflector,
name: "TargetGO",
type: typeof(GameObject),
value: new GameObjectRef(targetGoEx.GameObject!.GetInstanceID())
);

var componentModification = SerializedMember.FromValue(
reflector: reflector,
name: "DataPopulationTestScript",
type: typeof(DataPopulationTestScript),
value: new ComponentRef(addCompEx.Component!.GetInstanceID())
);

componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "materialField", type: typeof(Material), value: matRef));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "gameObjectField", type: typeof(GameObject), value: goRef));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "textureField", type: typeof(Texture2D), value: texRef));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "scriptableObjectField", type: typeof(DataPopulationTestScriptableObject), value: soRef));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "prefabField", type: typeof(GameObject), value: prefabRef));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "intField", type: typeof(int), value: 42));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "stringField", type: typeof(string), value: "Hello World"));

var matRefArrayItem = new ObjectRef(materialEx.Asset!.GetInstanceID());
var goRefArrayItem = new ObjectRef(targetGoEx.GameObject!.GetInstanceID());
var prefabRefArrayItem = new ObjectRef(prefabEx.Asset!.GetInstanceID());

componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "materialArray", type: typeof(Material[]), value: new object[] { matRefArrayItem, matRefArrayItem }));
componentModification.AddField(SerializedMember.FromValue(reflector: reflector, name: "gameObjectArray", type: typeof(GameObject[]), value: new object[] { goRefArrayItem, prefabRefArrayItem }));

goModification.AddField(componentModification);

var gameObjectDiffs = new SerializedMemberList { goModification };

var options = new System.Text.Json.JsonSerializerOptions { WriteIndented = true };
var gameObjectRefsJson = System.Text.Json.JsonSerializer.Serialize(new GameObjectRef[] { targetGoRef }, options);
var gameObjectDiffsJson = System.Text.Json.JsonSerializer.Serialize(gameObjectDiffs, options);

var json = JsonTestUtils.Fill(@"{
""gameObjectRefs"": {gameObjectRefs},
""gameObjectDiffs"": {gameObjectDiffs}
}",
new Dictionary<string, object?>
{
{ "{gameObjectRefs}", gameObjectRefsJson },
{ "{gameObjectDiffs}", gameObjectDiffsJson }
});

Debug.Log($"[DataPopulationTests] JSON Input: {json}");
return json;
}
);

modifyEx.AddChild(validateEx);
addCompEx.AddChild(modifyEx);
targetGoEx.AddChild(addCompEx);

materialEx
.Nest(textureEx)
.Nest(soEx)
.Nest(prefabSourceGoEx)
.Nest(prefabEx)
.Nest(targetGoEx);

materialEx.Execute();
yield return null;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading