summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram
diff options
context:
space:
mode:
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram')
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs2080
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs19
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs.meta11
4 files changed, 2121 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs
new file mode 100644
index 00000000..dfd288bd
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs
@@ -0,0 +1,2080 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.Serialization;
+using JetBrains.Annotations;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEditorInternal;
+using UnityEngine;
+using UnityEngine.Assertions;
+using VRC.Udon.Common;
+using VRC.Udon.Common.Interfaces;
+using VRC.Udon.ProgramSources;
+
+namespace VRC.Udon.Editor.ProgramSources
+{
+ public class UdonProgramAsset : AbstractUdonProgramSource, ISerializationCallbackReceiver
+ {
+ protected IUdonProgram program;
+
+ [SerializeField]
+ protected AbstractSerializedUdonProgramAsset serializedUdonProgramAsset;
+
+ public override AbstractSerializedUdonProgramAsset SerializedProgramAsset
+ {
+ get
+ {
+ AssetDatabase.TryGetGUIDAndLocalFileIdentifier(this, out string guid, out long _);
+ if(serializedUdonProgramAsset != null)
+ {
+ if(serializedUdonProgramAsset.name == guid)
+ {
+ return serializedUdonProgramAsset;
+ }
+
+ string oldSerializedUdonProgramAssetPath = Path.Combine("Assets", "SerializedUdonPrograms", $"{serializedUdonProgramAsset.name}.asset");
+ AssetDatabase.DeleteAsset(oldSerializedUdonProgramAssetPath);
+ }
+
+ string serializedUdonProgramAssetPath = Path.Combine("Assets", "SerializedUdonPrograms", $"{guid}.asset");
+
+ serializedUdonProgramAsset = (SerializedUdonProgramAsset)AssetDatabase.LoadAssetAtPath(
+ Path.Combine("Assets", "SerializedUdonPrograms", $"{guid}.asset"),
+ typeof(SerializedUdonProgramAsset)
+ );
+
+ if(serializedUdonProgramAsset != null)
+ {
+ return serializedUdonProgramAsset;
+ }
+
+ serializedUdonProgramAsset = CreateInstance<SerializedUdonProgramAsset>();
+ if(!AssetDatabase.IsValidFolder(Path.Combine("Assets", "SerializedUdonPrograms")))
+ {
+ AssetDatabase.CreateFolder("Assets", "SerializedUdonPrograms");
+ }
+
+ AssetDatabase.CreateAsset(serializedUdonProgramAsset, serializedUdonProgramAssetPath);
+ AssetDatabase.SaveAssets();
+
+ RefreshProgram();
+ AssetDatabase.SaveAssets();
+
+ AssetDatabase.Refresh();
+
+ return serializedUdonProgramAsset;
+ }
+ }
+
+ public sealed override void RunEditorUpdate(UdonBehaviour udonBehaviour, ref bool dirty)
+ {
+ if(program == null && serializedUdonProgramAsset != null)
+ {
+ program = serializedUdonProgramAsset.RetrieveProgram();
+ }
+
+ if(program == null)
+ {
+ RefreshProgram();
+ }
+
+ DrawProgramSourceGUI(udonBehaviour, ref dirty);
+
+ if(dirty)
+ {
+ EditorUtility.SetDirty(this);
+ }
+ }
+
+ protected virtual void DrawProgramSourceGUI(UdonBehaviour udonBehaviour, ref bool dirty)
+ {
+ DrawPublicVariables(udonBehaviour, ref dirty);
+ DrawProgramDisassembly();
+ }
+
+ public sealed override void RefreshProgram()
+ {
+ if(Application.isPlaying)
+ {
+ return;
+ }
+
+ RefreshProgramImpl();
+
+ SerializedProgramAsset.StoreProgram(program);
+ if (this != null)
+ {
+ EditorUtility.SetDirty(this);
+ }
+ }
+
+ protected virtual void RefreshProgramImpl()
+ {
+ }
+
+ [PublicAPI]
+ protected void DrawInteractionArea(UdonBehaviour udonBehaviour)
+ {
+ ImmutableArray<string> exportedSymbols = program.EntryPoints.GetExportedSymbols();
+ if (exportedSymbols.Contains("_interact"))
+ {
+ EditorGUILayout.LabelField("Interaction", EditorStyles.boldLabel);
+ EditorGUI.indentLevel++;
+
+ if(udonBehaviour != null)
+ {
+ udonBehaviour.interactText = EditorGUILayout.TextField("Interaction Text", udonBehaviour.interactText);
+ udonBehaviour.proximity = EditorGUILayout.Slider("Proximity", udonBehaviour.proximity, 0f, 100f);
+ udonBehaviour.interactTextPlacement = (Transform)EditorGUILayout.ObjectField("Text Placement", udonBehaviour.interactTextPlacement, typeof(Transform), true);
+ }
+ else
+ {
+ using(new EditorGUI.DisabledScope(true))
+ {
+ EditorGUILayout.TextField("Interaction Text", "Use");
+ EditorGUILayout.Slider("Proximity", 2.0f, 0f, 100f);
+ EditorGUILayout.ObjectField("Text Placement", null, typeof(Transform), true);
+ }
+ }
+
+
+
+ EditorGUI.indentLevel--;
+ }
+ }
+
+ [PublicAPI]
+ protected void DrawPublicVariables(UdonBehaviour udonBehaviour, ref bool dirty)
+ {
+ IUdonVariableTable publicVariables = null;
+ if(udonBehaviour != null)
+ {
+ publicVariables = udonBehaviour.publicVariables;
+ }
+
+ EditorGUILayout.LabelField("Public Variables", EditorStyles.boldLabel);
+ EditorGUI.indentLevel++;
+ if(program?.SymbolTable == null)
+ {
+ EditorGUILayout.LabelField("No public variables.");
+ EditorGUI.indentLevel--;
+ return;
+ }
+
+ IUdonSymbolTable symbolTable = program.SymbolTable;
+ // Remove non-exported public variables
+ if(publicVariables != null)
+ {
+ foreach(string publicVariableSymbol in publicVariables.VariableSymbols.ToArray())
+ {
+ if(!symbolTable.HasExportedSymbol(publicVariableSymbol))
+ {
+ publicVariables.RemoveVariable(publicVariableSymbol);
+ }
+ }
+ }
+
+ ImmutableArray<string> exportedSymbolNames = symbolTable.GetExportedSymbols();
+ if(exportedSymbolNames.Length <= 0)
+ {
+ EditorGUILayout.LabelField("No public variables.");
+ EditorGUI.indentLevel--;
+ return;
+ }
+
+ foreach(string exportedSymbol in exportedSymbolNames)
+ {
+ Type symbolType = symbolTable.GetSymbolType(exportedSymbol);
+ if(publicVariables == null)
+ {
+ DrawPublicVariableField(exportedSymbol, GetPublicVariableDefaultValue(exportedSymbol, symbolType), symbolType, ref dirty, false);
+ continue;
+ }
+
+ if(!publicVariables.TryGetVariableType(exportedSymbol, out Type declaredType) || declaredType != symbolType)
+ {
+ publicVariables.RemoveVariable(exportedSymbol);
+ if(!publicVariables.TryAddVariable(CreateUdonVariable(exportedSymbol, GetPublicVariableDefaultValue(exportedSymbol, declaredType), symbolType)))
+ {
+ EditorGUILayout.LabelField($"Error drawing field for symbol '{exportedSymbol}'.");
+ continue;
+ }
+ }
+
+ if(!publicVariables.TryGetVariableValue(exportedSymbol, out object variableValue))
+ {
+ variableValue = GetPublicVariableDefaultValue(exportedSymbol, declaredType);
+ }
+
+ variableValue = DrawPublicVariableField(exportedSymbol, variableValue, symbolType, ref dirty, true);
+ if(!dirty)
+ {
+ continue;
+ }
+
+ Undo.RecordObject(udonBehaviour, "Modify Public Variable");
+
+ if(!publicVariables.TrySetVariableValue(exportedSymbol, variableValue))
+ {
+ if(!publicVariables.TryAddVariable(CreateUdonVariable(exportedSymbol, variableValue, symbolType)))
+ {
+ Debug.LogError($"Failed to set public variable '{exportedSymbol}' value.");
+ }
+ }
+
+ EditorSceneManager.MarkSceneDirty(udonBehaviour.gameObject.scene);
+
+ if(PrefabUtility.IsPartOfPrefabInstance(udonBehaviour))
+ {
+ PrefabUtility.RecordPrefabInstancePropertyModifications(udonBehaviour);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ private static IUdonVariable CreateUdonVariable(string symbolName, object value, Type declaredType)
+ {
+ Type udonVariableType = typeof(UdonVariable<>).MakeGenericType(declaredType);
+ return (IUdonVariable)Activator.CreateInstance(udonVariableType, symbolName, value);
+ }
+
+ [PublicAPI]
+ protected virtual object GetPublicVariableDefaultValue(string symbol, Type type)
+ {
+ return null;
+ }
+
+ [PublicAPI]
+ protected void DrawProgramDisassembly()
+ {
+ try
+ {
+ EditorGUILayout.LabelField("Disassembled Program", EditorStyles.boldLabel);
+ using(new EditorGUI.DisabledScope(true))
+ {
+ string[] disassembledProgram = UdonEditorManager.Instance.DisassembleProgram(program);
+ EditorGUILayout.TextArea(string.Join("\n", disassembledProgram));
+ }
+ }
+ catch(Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+
+ [NonSerialized]
+ private readonly Dictionary<string, bool> _arrayStates = new Dictionary<string, bool>();
+
+ protected virtual object DrawPublicVariableField(string symbol, object variableValue, Type variableType, ref bool dirty, bool enabled)
+ {
+ using(new EditorGUI.DisabledScope(!enabled))
+ {
+ // ReSharper disable RedundantNameQualifier
+ if(!variableType.IsInstanceOfType(variableValue))
+ {
+ if(variableType.IsValueType)
+ {
+ variableValue = Activator.CreateInstance(variableType);
+ }
+ else
+ {
+ variableValue = null;
+ }
+ }
+
+ EditorGUILayout.BeginHorizontal();
+ if(typeof(UnityEngine.Object).IsAssignableFrom(variableType))
+ {
+ UnityEngine.Object unityEngineObjectValue = (UnityEngine.Object)variableValue;
+ EditorGUI.BeginChangeCheck();
+ Rect fieldRect = EditorGUILayout.GetControlRect();
+ variableValue = EditorGUI.ObjectField(fieldRect, symbol, unityEngineObjectValue, variableType, true);
+
+ if(variableValue == null && (variableType == typeof(GameObject) || variableType == typeof(Transform) ||
+ variableType == typeof(UdonBehaviour)))
+ {
+ EditorGUI.LabelField(
+ fieldRect,
+ new GUIContent(symbol),
+ new GUIContent("Self (" + variableType.Name + ")", AssetPreview.GetMiniTypeThumbnail(variableType)),
+ EditorStyles.objectField);
+ }
+
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(string))
+ {
+ string stringValue = (string)variableValue;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.TextField(symbol, stringValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(string[]))
+ {
+ string[] valueArray = (string[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.TextField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : "");
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(float))
+ {
+ float floatValue = (float?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.FloatField(symbol, floatValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(float[]))
+ {
+ float[] valueArray = (float[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.FloatField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(int))
+ {
+ int intValue = (int?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.IntField(symbol, intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(int[]))
+ {
+ int[] valueArray = (int[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(short))
+ {
+ short intValue = (short?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (short)EditorGUILayout.IntField(symbol, intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(short[]))
+ {
+ short[] valueArray = (short[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (short)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(long))
+ {
+ long intValue = (long?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (long)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(long[]))
+ {
+ long[] valueArray = (long[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(uint))
+ {
+ uint intValue = (uint?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (uint)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(uint[]))
+ {
+ uint[] valueArray = (uint[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (uint)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ushort))
+ {
+ ushort intValue = (ushort?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (ushort)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ushort[]))
+ {
+ ushort[] valueArray = (ushort[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (ushort)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ulong))
+ {
+ ulong intValue = (ulong?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (ulong)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ulong[]))
+ {
+ ulong[] valueArray = (ulong[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (ulong)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(byte))
+ {
+ byte intValue = (byte?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (byte)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(byte[]))
+ {
+ byte[] valueArray = (byte[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (byte)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(sbyte))
+ {
+ sbyte intValue = (sbyte?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (sbyte)EditorGUILayout.IntField(symbol, (int)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(sbyte[]))
+ {
+ sbyte[] valueArray = (sbyte[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (sbyte)EditorGUILayout.IntField(
+ $"{i}:",
+ valueArray.Length > i ? (int)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(double))
+ {
+ double intValue = (double?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (double)EditorGUILayout.DoubleField(symbol, intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(double[]))
+ {
+ double[] valueArray = (double[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.DoubleField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(decimal))
+ {
+ decimal intValue = (decimal?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (decimal)EditorGUILayout.DoubleField(symbol, (double)intValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(decimal[]))
+ {
+ decimal[] valueArray = (decimal[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = (decimal)EditorGUILayout.DoubleField(
+ $"{i}:",
+ valueArray.Length > i ? (double)valueArray[i] : 0);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(bool))
+ {
+ bool boolValue = (bool?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.Toggle(symbol, boolValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(bool[]))
+ {
+ bool[] valueArray = (bool[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.Toggle(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : false);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Vector2))
+ {
+ Vector2 vector2Value = (Vector2?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.Vector2Field(symbol, vector2Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Vector2[]))
+ {
+ Vector2[] valueArray = (Vector2[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.Vector2Field(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Vector2.zero);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Vector3))
+ {
+ Vector3 vector3Value = (Vector3?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.Vector3Field(symbol, vector3Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Vector3[]))
+ {
+ Vector3[] valueArray = (Vector3[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.Vector3Field(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Vector3.zero);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Vector2Int))
+ {
+ Vector2Int vector2IntValue = (Vector2Int?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ Vector2 vector2Value = EditorGUILayout.Vector2Field(symbol, vector2IntValue);
+ variableValue = new Vector2Int((int)vector2Value.x, (int)vector2Value.y);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Vector2Int[]))
+ {
+ Vector2Int[] valueArray = (Vector2Int[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ Vector2 vector2Value = EditorGUILayout.Vector2Field(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Vector2.zero);
+ valueArray[i] = new Vector2Int((int)vector2Value.x, (int)vector2Value.y);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Vector3Int))
+ {
+ Vector3Int vector3IntValue = (Vector3Int?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ Vector3 vector3Value = EditorGUILayout.Vector3Field(symbol, vector3IntValue);
+ variableValue = new Vector3Int((int)vector3Value.x, (int)vector3Value.y, (int)vector3Value.z);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Vector3Int[]))
+ {
+ Vector3Int[] valueArray = (Vector3Int[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ Vector3 vector3Value = EditorGUILayout.Vector3Field(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Vector3.zero);
+ valueArray[i] = new Vector3Int((int)vector3Value.x, (int)vector3Value.y,
+ (int)vector3Value.z);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Vector4))
+ {
+ Vector4 vector4Value = (Vector4?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.Vector4Field(symbol, vector4Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Vector4[]))
+ {
+ Vector4[] valueArray = (Vector4[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.Vector4Field(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Vector4.zero);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Quaternion))
+ {
+ Quaternion quaternionValue = (Quaternion?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ Vector4 quaternionVector4 = EditorGUILayout.Vector4Field(symbol, new Vector4(quaternionValue.x, quaternionValue.y, quaternionValue.z, quaternionValue.w));
+ variableValue = new Quaternion(quaternionVector4.x, quaternionVector4.y, quaternionVector4.z, quaternionVector4.w);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Quaternion[]))
+ {
+ Quaternion[] valueArray = (Quaternion[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ Vector4 vector4 = EditorGUILayout.Vector4Field(
+ $"{i}:",
+ valueArray.Length > i ? new Vector4(valueArray[i].x, valueArray[i].y, valueArray[i].z, valueArray[i].w) : Vector4.zero);
+
+ valueArray[i] = new Quaternion(vector4.x, vector4.y, vector4.z, vector4.w);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Gradient))
+ {
+ Gradient color2Value = variableValue as Gradient;
+ if (color2Value == null) color2Value = new Gradient();
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.GradientField(symbol, color2Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Gradient[]))
+ {
+ Gradient[] valueArray = (Gradient[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ Gradient g = valueArray.Length > i ? (valueArray[i]) : new Gradient();
+ if (g == null) g = new Gradient();
+ valueArray[i] = EditorGUILayout.GradientField($"{i}:", g);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(AnimationCurve))
+ {
+ AnimationCurve curve2Value = variableValue as AnimationCurve;
+ if (curve2Value == null) curve2Value = new AnimationCurve();
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.CurveField(symbol, curve2Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(AnimationCurve[]))
+ {
+ AnimationCurve[] valueArray = (AnimationCurve[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ AnimationCurve curve = valueArray.Length > i ? (valueArray[i]) : new AnimationCurve();
+ if (curve == null) curve = new AnimationCurve();
+ valueArray[i] = EditorGUILayout.CurveField($"{i}:", curve);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Color))
+ {
+ Color color2Value = (Color?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.ColorField(symbol, color2Value);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Color[]))
+ {
+ Color[] valueArray = (Color[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.ColorField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : Color.white);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(UnityEngine.Color32))
+ {
+ Color32 colorValue = (Color32?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ variableValue = (Color32)EditorGUILayout.ColorField(symbol, colorValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Color32[]))
+ {
+ Color32[] valueArray = (Color32[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+ EditorGUILayout.BeginVertical();
+
+ EditorGUI.BeginChangeCheck();
+ // Show Foldout Header
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ // Save foldout state
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.ColorField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i] : (Color32)Color.white);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ParticleSystem.MinMaxCurve))
+ {
+ ParticleSystem.MinMaxCurve minMaxCurve = (ParticleSystem.MinMaxCurve?)variableValue ?? default;
+ EditorGUI.BeginChangeCheck();
+ float multiplier = minMaxCurve.curveMultiplier;
+ AnimationCurve minCurve = minMaxCurve.curveMin;
+ AnimationCurve maxCurve = minMaxCurve.curveMax;
+ EditorGUILayout.BeginVertical();
+ EditorGUILayout.LabelField(symbol);
+ EditorGUI.indentLevel++;
+ multiplier = EditorGUILayout.FloatField("Multiplier", multiplier);
+ minCurve = EditorGUILayout.CurveField("Min Curve", minCurve);
+ maxCurve = EditorGUILayout.CurveField("Max Curve", maxCurve);
+ EditorGUI.indentLevel--;
+ EditorGUILayout.EndVertical();
+ variableValue = new ParticleSystem.MinMaxCurve(multiplier, minCurve, maxCurve);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(ParticleSystem.MinMaxCurve[]))
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.BeginVertical();
+ ParticleSystem.MinMaxCurve[] valueArray = (ParticleSystem.MinMaxCurve[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+
+ showArray = EditorGUILayout.Foldout(showArray, symbol, true);
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ ParticleSystem.MinMaxCurve minMaxCurve = (ParticleSystem.MinMaxCurve)valueArray[i];
+ float multiplier = minMaxCurve.curveMultiplier;
+ AnimationCurve minCurve = minMaxCurve.curveMin;
+ AnimationCurve maxCurve = minMaxCurve.curveMax;
+ EditorGUILayout.BeginVertical();
+ EditorGUI.indentLevel++;
+ multiplier = EditorGUILayout.FloatField("Multiplier", multiplier);
+ minCurve = EditorGUILayout.CurveField("Min Curve", minCurve);
+ maxCurve = EditorGUILayout.CurveField("Max Curve", maxCurve);
+ EditorGUI.indentLevel--;
+ EditorGUILayout.EndVertical();
+ valueArray[i] = new ParticleSystem.MinMaxCurve(multiplier, minCurve, maxCurve);
+ EditorGUILayout.Space();
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType.IsEnum)
+ {
+ Enum enumValue = (Enum)variableValue;
+ GUI.SetNextControlName("NodeField");
+ EditorGUI.BeginChangeCheck();
+ variableValue = EditorGUILayout.EnumPopup(symbol, enumValue);
+ if(EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ // ReSharper disable once PossibleNullReferenceException
+ else if(variableType.IsArray && variableType.GetElementType().IsEnum)
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.LabelField(symbol);
+ EditorGUILayout.BeginVertical();
+ Enum[] valueArray = (Enum[])variableValue;
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if(_arrayStates.ContainsKey(symbol))
+ {
+ showArray = _arrayStates[symbol];
+ }
+ else
+ {
+ _arrayStates.Add(symbol, false);
+ }
+
+ showArray = EditorGUILayout.Foldout(showArray, symbol);
+ _arrayStates[symbol] = showArray;
+
+ if(showArray)
+ {
+ EditorGUI.indentLevel++;
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1
+ );
+ EditorGUILayout.Space();
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if(valueArray != null && valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.EnumPopup(
+ $"{i}:",
+ valueArray[i]);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if(variableType == typeof(Type))
+ {
+ Type typeValue = (Type)variableValue;
+ EditorGUILayout.LabelField(symbol, typeValue == null ? $"Type = null" : $"Type = {typeValue.Name}");
+ }
+
+ else if(variableType.IsArray && typeof(UnityEngine.Object).IsAssignableFrom(variableType.GetElementType()))
+ {
+ Type elementType = variableType.GetElementType();
+ Assert.IsNotNull(elementType);
+
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.BeginVertical();
+ GUI.SetNextControlName("NodeField");
+
+ bool showArray = false;
+ if (_arrayStates.ContainsKey(symbol))
+ showArray = _arrayStates[symbol];
+ else
+ _arrayStates.Add(symbol, false);
+ showArray = EditorGUILayout.Foldout( showArray, symbol, true );
+ _arrayStates[symbol] = showArray;
+
+ if(variableValue == null)
+ {
+ variableValue = Array.CreateInstance(elementType, 0);
+ }
+
+ UnityEngine.Object[] valueArray = (UnityEngine.Object[])variableValue;
+
+ if (showArray)
+ {
+ EditorGUI.indentLevel++;
+
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray.Length > 0 ? valueArray.Length : 1);
+
+ Array.Resize(ref valueArray, newSize);
+ Assert.IsNotNull(valueArray);
+
+ if(valueArray.Length > 0)
+ {
+ for(int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ valueArray[i] = EditorGUILayout.ObjectField($"{i}:", valueArray.Length > i ? valueArray[i] : null, variableType.GetElementType(), true);
+ }
+ }
+
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.EndVertical();
+ if(EditorGUI.EndChangeCheck())
+ {
+ Array destinationArray = Array.CreateInstance(elementType, valueArray.Length);
+ Array.Copy(valueArray, destinationArray, valueArray.Length);
+
+ variableValue = destinationArray;
+
+ dirty = true;
+ }
+ }
+ else if (variableType == typeof(VRC.SDKBase.VRCUrl))
+ {
+ if(variableValue == null)
+ variableValue = new VRC.SDKBase.VRCUrl("");
+
+ VRC.SDKBase.VRCUrl url = (VRC.SDKBase.VRCUrl)variableValue;
+ EditorGUI.BeginChangeCheck();
+ variableValue = new VRC.SDKBase.VRCUrl(EditorGUILayout.TextField(symbol, url.Get()));
+
+ if (EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else if (variableType == typeof(VRC.SDKBase.VRCUrl[]))
+ {
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.BeginVertical();
+
+ GUI.SetNextControlName("NodeField");
+ bool showArray = false;
+ if (_arrayStates.ContainsKey(symbol))
+ showArray = _arrayStates[symbol];
+ else
+ _arrayStates.Add(symbol, false);
+ showArray = EditorGUILayout.Foldout( showArray, symbol, true );
+ _arrayStates[symbol] = showArray;
+
+ VRC.SDKBase.VRCUrl[] valueArray = (VRC.SDKBase.VRCUrl[])variableValue;
+
+ if (showArray)
+ {
+ EditorGUI.indentLevel++;
+ EditorGUILayout.Space();
+ int newSize = EditorGUILayout.IntField(
+ "size:",
+ valueArray != null && valueArray.Length > 0 ? valueArray.Length : 1);
+ newSize = newSize >= 0 ? newSize : 0;
+ Array.Resize(ref valueArray, newSize);
+
+ if (valueArray != null && valueArray.Length > 0)
+ {
+ for (int i = 0; i < valueArray.Length; i++)
+ {
+ GUI.SetNextControlName("NodeField");
+ if (valueArray[i] == null)
+ valueArray[i] = new VRC.SDKBase.VRCUrl("");
+
+ valueArray[i] = new VRC.SDKBase.VRCUrl(
+ EditorGUILayout.TextField(
+ $"{i}:",
+ valueArray.Length > i ? valueArray[i].Get() : ""));
+ }
+ }
+ EditorGUI.indentLevel--;
+ }
+
+ EditorGUILayout.EndVertical();
+ if (EditorGUI.EndChangeCheck())
+ {
+ variableValue = valueArray;
+ dirty = true;
+ }
+ }
+ else if (variableType == typeof(LayerMask))
+ {
+ LayerMask maskValue = (LayerMask)variableValue;
+ GUI.SetNextControlName("NodeField");
+ EditorGUI.BeginChangeCheck();
+ EditorGUILayout.LabelField(symbol);
+ // Using workaround from http://answers.unity.com/answers/1387522/view.html
+ LayerMask tempMask = EditorGUILayout.MaskField(InternalEditorUtility.LayerMaskToConcatenatedLayersMask(maskValue), InternalEditorUtility.layers);
+ variableValue = InternalEditorUtility.ConcatenatedLayersMaskToLayerMask(tempMask);
+ if (EditorGUI.EndChangeCheck())
+ {
+ dirty = true;
+ }
+ }
+ else
+ {
+ EditorGUILayout.LabelField(symbol + " no defined editor for type of " + variableType);
+ }
+ // ReSharper restore RedundantNameQualifier
+
+ IUdonSyncMetadata sync = program.SyncMetadataTable.GetSyncMetadataFromSymbol(symbol);
+ if(sync != null)
+ {
+ GUILayout.Label($"sync{sync.Properties[0].InterpolationAlgorithm.ToString()}", GUILayout.Width(80));
+ }
+ }
+
+ EditorGUILayout.EndHorizontal();
+
+ return variableValue;
+ }
+
+ #region Serialization Methods
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ OnBeforeSerialize();
+ }
+
+ [PublicAPI]
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ [PublicAPI]
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+
+ #endregion
+ }
+}
diff --git a/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs.meta b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs.meta
new file mode 100644
index 00000000..2495f546
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAsset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 264ec3c8a1d423f42a144da0df6c5ebe
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs
new file mode 100644
index 00000000..56a21a85
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs
@@ -0,0 +1,19 @@
+using UnityEditor;
+
+namespace VRC.Udon.Editor.ProgramSources
+{
+ [CustomEditor(typeof(UdonProgramAsset))]
+ public class UdonProgramAssetEditor : UnityEditor.Editor
+ {
+ public override void OnInspectorGUI()
+ {
+ bool dirty = false;
+ UdonProgramAsset programAsset = (UdonProgramAsset)target;
+ programAsset.RunEditorUpdate(null, ref dirty);
+ if(dirty)
+ {
+ EditorUtility.SetDirty(target);
+ }
+ }
+ }
+}
diff --git a/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs.meta b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs.meta
new file mode 100644
index 00000000..3f085920
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Editor/ProgramSources/UdonProgram/UdonProgramAssetEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 41d70977fa7936441afe41442f1862b2
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: