summaryrefslogtreecommitdiff
path: root/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration
diff options
context:
space:
mode:
authortylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
committertylermurphy534 <tylermurphy534@gmail.com>2022-11-06 15:12:42 -0500
commiteb84bb298d2b95aec7b2ae12cbf25ac64f25379a (patch)
treeefd616a157df06ab661c6d56651853431ac6b08b /VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration
downloadunityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.gz
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.tar.bz2
unityprojects-eb84bb298d2b95aec7b2ae12cbf25ac64f25379a.zip
move to self host
Diffstat (limited to 'VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration')
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs738
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs400
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport.meta8
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs70
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs467
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs75
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs33
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs76
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs72
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs78
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters.meta8
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs73
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs57
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs61
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs95
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs61
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs81
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs57
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs58
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs106
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs121
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs55
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs61
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs61
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs47
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs57
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs59
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs61
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs156
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects.meta8
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs30
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs31
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs30
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs175
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs62
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs62
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs63
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs60
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs60
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs60
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs160
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs100
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta11
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs2788
-rw-r--r--VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta11
83 files changed, 7481 insertions, 0 deletions
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs
new file mode 100644
index 00000000..672d524f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs
@@ -0,0 +1,738 @@
+//-----------------------------------------------------------------------
+// <copyright file="AOTSupportScanner.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+#if UNITY_EDITOR
+
+namespace VRC.Udon.Serialization.OdinSerializer.Editor
+{
+ using Utilities;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using UnityEditor;
+ using UnityEditor.SceneManagement;
+ using UnityEngine;
+ using System.Reflection;
+ using UnityEngine.SceneManagement;
+ using System.Collections;
+
+ public sealed class AOTSupportScanner : IDisposable
+ {
+ private bool scanning;
+ private bool allowRegisteringScannedTypes;
+ private HashSet<Type> seenSerializedTypes = new HashSet<Type>();
+
+ private static System.Diagnostics.Stopwatch smartProgressBarWatch = System.Diagnostics.Stopwatch.StartNew();
+ private static int smartProgressBarDisplaysSinceLastUpdate = 0;
+
+ private static readonly MethodInfo PlayerSettings_GetPreloadedAssets_Method = typeof(PlayerSettings).GetMethod("GetPreloadedAssets", BindingFlags.Public | BindingFlags.Static, null, Type.EmptyTypes, null);
+ private static readonly PropertyInfo Debug_Logger_Property = typeof(Debug).GetProperty("unityLogger") ?? typeof(Debug).GetProperty("logger");
+
+ public void BeginScan()
+ {
+ this.scanning = true;
+ allowRegisteringScannedTypes = false;
+
+ this.seenSerializedTypes.Clear();
+
+ FormatterLocator.OnLocatedEmittableFormatterForType += this.OnLocatedEmitType;
+ FormatterLocator.OnLocatedFormatter += this.OnLocatedFormatter;
+ Serializer.OnSerializedType += this.OnSerializedType;
+ }
+
+ public bool ScanPreloadedAssets(bool showProgressBar)
+ {
+ // The API does not exist in this version of Unity
+ if (PlayerSettings_GetPreloadedAssets_Method == null) return true;
+
+ UnityEngine.Object[] assets = (UnityEngine.Object[])PlayerSettings_GetPreloadedAssets_Method.Invoke(null, null);
+
+ if (assets == null) return true;
+
+ try
+ {
+ for (int i = 0; i < assets.Length; i++)
+ {
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning preloaded assets for AOT support", (i + 1) + " / " + assets.Length, (float)i / assets.Length))
+ {
+ return false;
+ }
+
+ var asset = assets[i];
+
+ if (asset == null) continue;
+
+ if (AssetDatabase.Contains(asset))
+ {
+ // Scan the asset and all its dependencies
+ var path = AssetDatabase.GetAssetPath(asset);
+ this.ScanAsset(path, true);
+ }
+ else
+ {
+ // Just scan the asset
+ this.ScanObject(asset);
+ }
+ }
+ }
+ finally
+ {
+ if (showProgressBar)
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ return true;
+ }
+
+ public bool ScanAssetBundle(string bundle)
+ {
+ string[] assets = AssetDatabase.GetAssetPathsFromAssetBundle(bundle);
+
+ foreach (var asset in assets)
+ {
+ this.ScanAsset(asset, true);
+ }
+
+ return true;
+ }
+
+ public bool ScanAllAssetBundles(bool showProgressBar)
+ {
+ try
+ {
+ string[] bundles = AssetDatabase.GetAllAssetBundleNames();
+
+ for (int i = 0; i < bundles.Length; i++)
+ {
+ var bundle = bundles[i];
+
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning asset bundles for AOT support", bundle, (float)i / bundles.Length))
+ {
+ return false;
+ }
+
+ this.ScanAssetBundle(bundle);
+ }
+ }
+ finally
+ {
+ if (showProgressBar)
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ return true;
+ }
+
+ public bool ScanAllAddressables(bool includeAssetDependencies, bool showProgressBar)
+ {
+ // We don't know whether the addressables package is installed or not. So... needs must.
+ // Our only real choice is to utilize reflection that's stocked to the brim with failsafes
+ // and error logging.
+ //
+ // Truly, the code below should not have needed to be written.
+
+ // The following section is the code as it would be without reflection. Please modify this
+ // code reference to be accurate if the reflection code is changed.
+
+ /*
+
+ var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings;
+
+ if (settings != null && settings.groups != null)
+ {
+ foreach (AddressableAssetGroup group in settings.groups)
+ {
+ if (group.HasSchema(typeof(PlayerDataGroupSchema))) continue;
+
+ List<AddressableAssetEntry> results = new List<AddressableAssetEntry>();
+
+ group.GatherAllAssets(results, true, true, true, null);
+
+ foreach (var result in results)
+ {
+ this.ScanAsset(result.AssetPath, includeAssetDependencies);
+ }
+ }
+ }
+
+ */
+
+ bool progressBarWasDisplayed = false;
+
+ try
+ {
+ Type AddressableAssetSettingsDefaultObject_Type = TwoWaySerializationBinder.Default.BindToType("UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject");
+ if (AddressableAssetSettingsDefaultObject_Type == null) return true;
+ PropertyInfo AddressableAssetSettingsDefaultObject_Settings = AddressableAssetSettingsDefaultObject_Type.GetProperty("Settings");
+ if (AddressableAssetSettingsDefaultObject_Settings == null) throw new NotSupportedException("AddressableAssetSettingsDefaultObject.Settings property not found");
+ ScriptableObject settings = (ScriptableObject)AddressableAssetSettingsDefaultObject_Settings.GetValue(null, null);
+
+ if (settings == null) return true;
+
+ Type AddressableAssetSettings_Type = settings.GetType();
+ PropertyInfo AddressableAssetSettings_groups = AddressableAssetSettings_Type.GetProperty("groups");
+ if (AddressableAssetSettings_groups == null) throw new NotSupportedException("AddressableAssetSettings.groups property not found");
+
+ IList groups = (IList)AddressableAssetSettings_groups.GetValue(settings, null);
+
+ if (groups == null) return true;
+
+ Type PlayerDataGroupSchema_Type = TwoWaySerializationBinder.Default.BindToType("UnityEditor.AddressableAssets.Settings.GroupSchemas.PlayerDataGroupSchema");
+ if (PlayerDataGroupSchema_Type == null) throw new NotSupportedException("PlayerDataGroupSchema type not found");
+
+ Type AddressableAssetGroup_Type = null;
+ MethodInfo AddressableAssetGroup_HasSchema = null;
+ MethodInfo AddressableAssetGroup_GatherAllAssets = null;
+
+ Type AddressableAssetEntry_Type = TwoWaySerializationBinder.Default.BindToType("UnityEditor.AddressableAssets.Settings.AddressableAssetEntry");
+ if (AddressableAssetEntry_Type == null) throw new NotSupportedException("AddressableAssetEntry type not found");
+ Type List_AddressableAssetEntry_Type = typeof(List<>).MakeGenericType(AddressableAssetEntry_Type);
+ Type Func_AddressableAssetEntry_bool_Type = typeof(Func<,>).MakeGenericType(AddressableAssetEntry_Type, typeof(bool));
+ PropertyInfo AddressableAssetEntry_AssetPath = AddressableAssetEntry_Type.GetProperty("AssetPath");
+ if (AddressableAssetEntry_AssetPath == null) throw new NotSupportedException("AddressableAssetEntry.AssetPath property not found");
+
+ foreach (object groupObj in groups)
+ {
+ ScriptableObject group = (ScriptableObject)groupObj;
+ if (group == null) continue;
+
+ string groupName = group.name;
+
+ if (AddressableAssetGroup_Type == null)
+ {
+ AddressableAssetGroup_Type = group.GetType();
+ AddressableAssetGroup_HasSchema = AddressableAssetGroup_Type.GetMethod("HasSchema", Flags.InstancePublic, null, new Type[] { typeof(Type) }, null);
+ if (AddressableAssetGroup_HasSchema == null) throw new NotSupportedException("AddressableAssetGroup.HasSchema(Type type) method not found");
+ AddressableAssetGroup_GatherAllAssets = AddressableAssetGroup_Type.GetMethod("GatherAllAssets", Flags.InstancePublic, null, new Type[] { List_AddressableAssetEntry_Type, typeof(bool), typeof(bool), typeof(bool), Func_AddressableAssetEntry_bool_Type }, null);
+ if (AddressableAssetGroup_GatherAllAssets == null) throw new NotSupportedException("AddressableAssetGroup.GatherAllAssets(List<AddressableAssetEntry> results, bool includeSelf, bool recurseAll, bool includeSubObjects, Func<AddressableAssetEntry, bool> entryFilter) method not found");
+ }
+
+ bool hasPlayerDataGroupSchema = (bool)AddressableAssetGroup_HasSchema.Invoke(group, new object[] { PlayerDataGroupSchema_Type });
+ if (hasPlayerDataGroupSchema) continue; // Skip this group, since it contains all the player data such as resources and build scenes, and we're scanning that separately
+
+ IList results = (IList)Activator.CreateInstance(List_AddressableAssetEntry_Type);
+
+ AddressableAssetGroup_GatherAllAssets.Invoke(group, new object[] { results, true, true, true, null });
+
+ for (int i = 0; i < results.Count; i++)
+ {
+ object entry = (object)results[i];
+ if (entry == null) continue;
+ string assetPath = (string)AddressableAssetEntry_AssetPath.GetValue(entry, null);
+
+ if (showProgressBar)
+ {
+ progressBarWasDisplayed = true;
+
+ if (DisplaySmartUpdatingCancellableProgressBar("Scanning addressables for AOT support", groupName + ": " + assetPath, (float)i / results.Count))
+ {
+ return false;
+ }
+ }
+
+ // Finally!
+ this.ScanAsset(assetPath, includeAssetDependencies);
+ }
+ }
+ }
+ catch (NotSupportedException ex)
+ {
+ Debug.LogWarning("Could not AOT scan Addressables assets due to missing APIs: " + ex.Message);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError("Scanning addressables failed with the following exception...");
+ Debug.LogException(ex);
+ }
+ finally
+ {
+ if (progressBarWasDisplayed)
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ return true;
+ }
+
+ public bool ScanAllResources(bool includeResourceDependencies, bool showProgressBar, List<string> resourcesPaths = null)
+ {
+ if (resourcesPaths == null)
+ {
+ resourcesPaths = new List<string>() {""};
+ }
+
+ try
+ {
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning resources for AOT support", "Loading resource assets", 0f))
+ {
+ return false;
+ }
+
+ var resourcesPathsSet = new HashSet<string>();
+ for (int i = 0; i < resourcesPaths.Count; i++)
+ {
+ var resourcesPath = resourcesPaths[i];
+
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Listing resources for AOT support", resourcesPath, (float)i / resourcesPaths.Count))
+ {
+ return false;
+ }
+
+ var resources = Resources.LoadAll(resourcesPath);
+
+ foreach (var resource in resources)
+ {
+ try
+ {
+ var assetPath = AssetDatabase.GetAssetPath(resource);
+
+ if (assetPath != null)
+ {
+ resourcesPathsSet.Add(assetPath);
+ }
+ }
+ catch (MissingReferenceException ex)
+ {
+ Debug.LogError("A resource threw a missing reference exception when scanning. Skipping resource and continuing scan.", resource);
+ Debug.LogException(ex, resource);
+ continue;
+ }
+ }
+ }
+
+ string[] resourcePaths = resourcesPathsSet.ToArray();
+
+ for (int i = 0; i < resourcePaths.Length; i++)
+ {
+ if (resourcePaths[i] == null) continue;
+
+ try
+ {
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning resource " + i + " for AOT support", resourcePaths[i], (float)i / resourcePaths.Length))
+ {
+ return false;
+ }
+
+ var assetPath = resourcePaths[i];
+
+ // Exclude editor-only resources
+ if (assetPath.ToLower().Contains("/editor/")) continue;
+
+ this.ScanAsset(assetPath, includeAssetDependencies: includeResourceDependencies);
+ }
+ catch (MissingReferenceException ex)
+ {
+ Debug.LogError("A resource '" + resourcePaths[i] + "' threw a missing reference exception when scanning. Skipping resource and continuing scan.");
+ Debug.LogException(ex);
+ continue;
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ if (showProgressBar)
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+ }
+
+ public bool ScanBuildScenes(bool includeSceneDependencies, bool showProgressBar)
+ {
+ var scenePaths = EditorBuildSettings.scenes
+ .Where(n => n.enabled)
+ .Select(n => n.path)
+ .ToArray();
+
+ return this.ScanScenes(scenePaths, includeSceneDependencies, showProgressBar);
+ }
+
+ public bool ScanScenes(string[] scenePaths, bool includeSceneDependencies, bool showProgressBar)
+ {
+ if (scenePaths.Length == 0) return true;
+
+ bool formerForceEditorModeSerialization = UnitySerializationUtility.ForceEditorModeSerialization;
+
+ try
+ {
+ UnitySerializationUtility.ForceEditorModeSerialization = true;
+
+ bool hasDirtyScenes = false;
+
+ for (int i = 0; i < EditorSceneManager.sceneCount; i++)
+ {
+ if (EditorSceneManager.GetSceneAt(i).isDirty)
+ {
+ hasDirtyScenes = true;
+ break;
+ }
+ }
+
+ if (hasDirtyScenes && !EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo())
+ {
+ return false;
+ }
+
+ var oldSceneSetup = EditorSceneManager.GetSceneManagerSetup();
+
+ try
+ {
+ for (int i = 0; i < scenePaths.Length; i++)
+ {
+ var scenePath = scenePaths[i];
+
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning scenes for AOT support", "Scene " + (i + 1) + "/" + scenePaths.Length + " - " + scenePath, (float)i / scenePaths.Length))
+ {
+ return false;
+ }
+
+ if (!System.IO.File.Exists(scenePath))
+ {
+ Debug.LogWarning("Skipped AOT scanning scene '" + scenePath + "' for a file not existing at the scene path.");
+ continue;
+ }
+
+ Scene openScene = default(Scene);
+
+ try
+ {
+ openScene = EditorSceneManager.OpenScene(scenePath, OpenSceneMode.Single);
+ }
+ catch
+ {
+ Debug.LogWarning("Skipped AOT scanning scene '" + scenePath + "' for throwing exceptions when trying to load it.");
+ continue;
+ }
+
+ var sceneGOs = Resources.FindObjectsOfTypeAll<GameObject>();
+
+ foreach (var go in sceneGOs)
+ {
+ if (go.scene != openScene) continue;
+
+ if ((go.hideFlags & HideFlags.DontSaveInBuild) == 0)
+ {
+ foreach (var component in go.GetComponents<ISerializationCallbackReceiver>())
+ {
+ try
+ {
+ this.allowRegisteringScannedTypes = true;
+ component.OnBeforeSerialize();
+
+ var prefabSupporter = component as ISupportsPrefabSerialization;
+
+ if (prefabSupporter != null)
+ {
+ // Also force a serialization of the object's prefab modifications, in case there are unknown types in there
+
+ List<UnityEngine.Object> objs = null;
+ var mods = UnitySerializationUtility.DeserializePrefabModifications(prefabSupporter.SerializationData.PrefabModifications, prefabSupporter.SerializationData.PrefabModificationsReferencedUnityObjects);
+ UnitySerializationUtility.SerializePrefabModifications(mods, ref objs);
+ }
+ }
+ finally
+ {
+ this.allowRegisteringScannedTypes = false;
+ }
+ }
+ }
+ }
+ }
+
+ // Load a new empty scene that will be unloaded immediately, just to be sure we completely clear all changes made by the scan
+ // Sometimes this fails for unknown reasons. In that case, swallow any exceptions, and just soldier on and hope for the best!
+ // Additionally, also eat any debug logs that happen here, because logged errors can stop the build process, and we don't want
+ // that to happen.
+
+ UnityEngine.ILogger logger = null;
+
+ if (Debug_Logger_Property != null)
+ {
+ logger = (UnityEngine.ILogger)Debug_Logger_Property.GetValue(null, null);
+ }
+
+ bool previous = true;
+
+ try
+ {
+ if (logger != null)
+ {
+ previous = logger.logEnabled;
+ logger.logEnabled = false;
+ }
+
+ EditorSceneManager.NewScene(NewSceneSetup.EmptyScene, NewSceneMode.Single);
+ }
+ catch { }
+ finally
+ {
+ if (logger != null)
+ {
+ logger.logEnabled = previous;
+ }
+ }
+
+ }
+ finally
+ {
+ if (oldSceneSetup != null && oldSceneSetup.Length > 0)
+ {
+ if (showProgressBar)
+ {
+ EditorUtility.DisplayProgressBar("Restoring scene setup", "", 1.0f);
+ }
+ EditorSceneManager.RestoreSceneManagerSetup(oldSceneSetup);
+ }
+ }
+
+ if (includeSceneDependencies)
+ {
+ for (int i = 0; i < scenePaths.Length; i++)
+ {
+ var scenePath = scenePaths[i];
+ if (showProgressBar && DisplaySmartUpdatingCancellableProgressBar("Scanning scene dependencies for AOT support", "Scene " + (i + 1) + "/" + scenePaths.Length + " - " + scenePath, (float)i / scenePaths.Length))
+ {
+ return false;
+ }
+
+ string[] dependencies = AssetDatabase.GetDependencies(scenePath, recursive: true);
+
+ foreach (var dependency in dependencies)
+ {
+ this.ScanAsset(dependency, includeAssetDependencies: false); // All dependencies of this asset were already included recursively by Unity
+ }
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ if (showProgressBar)
+ {
+ EditorUtility.ClearProgressBar();
+ }
+
+ UnitySerializationUtility.ForceEditorModeSerialization = formerForceEditorModeSerialization;
+ }
+ }
+
+ public bool ScanAsset(string assetPath, bool includeAssetDependencies)
+ {
+ if (assetPath.EndsWith(".unity"))
+ {
+ return this.ScanScenes(new string[] { assetPath }, includeAssetDependencies, false);
+ }
+
+ if (!(assetPath.EndsWith(".asset") || assetPath.EndsWith(".prefab")))
+ {
+ // ScanAsset can only scan .asset and .prefab assets.
+ return false;
+ }
+
+ bool formerForceEditorModeSerialization = UnitySerializationUtility.ForceEditorModeSerialization;
+
+ try
+ {
+ UnitySerializationUtility.ForceEditorModeSerialization = true;
+
+ var assets = AssetDatabase.LoadAllAssetsAtPath(assetPath);
+
+ if (assets == null || assets.Length == 0)
+ {
+ return false;
+ }
+
+ foreach (var asset in assets)
+ {
+ if (asset == null) continue;
+
+ this.ScanObject(asset);
+ }
+
+ if (includeAssetDependencies)
+ {
+ string[] dependencies = AssetDatabase.GetDependencies(assetPath, recursive: true);
+
+ foreach (var dependency in dependencies)
+ {
+ this.ScanAsset(dependency, includeAssetDependencies: false); // All dependencies were already included recursively by Unity
+ }
+ }
+
+ return true;
+ }
+ finally
+ {
+ UnitySerializationUtility.ForceEditorModeSerialization = formerForceEditorModeSerialization;
+ }
+ }
+
+ public void ScanObject(UnityEngine.Object obj)
+ {
+ if (obj is ISerializationCallbackReceiver)
+ {
+ bool formerForceEditorModeSerialization = UnitySerializationUtility.ForceEditorModeSerialization;
+
+ try
+ {
+ UnitySerializationUtility.ForceEditorModeSerialization = true;
+ this.allowRegisteringScannedTypes = true;
+ (obj as ISerializationCallbackReceiver).OnBeforeSerialize();
+ }
+ finally
+ {
+ this.allowRegisteringScannedTypes = false;
+ UnitySerializationUtility.ForceEditorModeSerialization = formerForceEditorModeSerialization;
+ }
+ }
+ }
+
+ public List<Type> EndScan()
+ {
+ if (!this.scanning) throw new InvalidOperationException("Cannot end a scan when scanning has not begun.");
+
+ var result = this.seenSerializedTypes.ToList();
+ this.Dispose();
+ return result;
+ }
+
+ public void Dispose()
+ {
+ if (this.scanning)
+ {
+ FormatterLocator.OnLocatedEmittableFormatterForType -= this.OnLocatedEmitType;
+ FormatterLocator.OnLocatedFormatter -= this.OnLocatedFormatter;
+ Serializer.OnSerializedType -= this.OnSerializedType;
+
+ this.scanning = false;
+ this.seenSerializedTypes.Clear();
+ this.allowRegisteringScannedTypes = false;
+ }
+ }
+
+ private void OnLocatedEmitType(Type type)
+ {
+ if (!AllowRegisterType(type)) return;
+
+ this.RegisterType(type);
+ }
+
+ private void OnSerializedType(Type type)
+ {
+ if (!AllowRegisterType(type)) return;
+
+ this.RegisterType(type);
+ }
+
+ private void OnLocatedFormatter(IFormatter formatter)
+ {
+ var type = formatter.SerializedType;
+
+ if (type == null) return;
+ if (!AllowRegisterType(type)) return;
+ this.RegisterType(type);
+ }
+
+ private static bool AllowRegisterType(Type type)
+ {
+ if (IsEditorOnlyAssembly(type.Assembly))
+ return false;
+
+ if (type.IsGenericType)
+ {
+ foreach (var parameter in type.GetGenericArguments())
+ {
+ if (!AllowRegisterType(parameter)) return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static bool IsEditorOnlyAssembly(Assembly assembly)
+ {
+ return EditorAssemblyNames.Contains(assembly.GetName().Name);
+ }
+
+ private static HashSet<string> EditorAssemblyNames = new HashSet<string>()
+ {
+ "Assembly-CSharp-Editor",
+ "Assembly-UnityScript-Editor",
+ "Assembly-Boo-Editor",
+ "Assembly-CSharp-Editor-firstpass",
+ "Assembly-UnityScript-Editor-firstpass",
+ "Assembly-Boo-Editor-firstpass",
+ typeof(Editor).Assembly.GetName().Name
+ };
+
+ private void RegisterType(Type type)
+ {
+ if (!this.allowRegisteringScannedTypes) return;
+ //if (type.IsAbstract || type.IsInterface) return;
+ if (type.IsGenericType && (type.IsGenericTypeDefinition || !type.IsFullyConstructedGenericType())) return;
+
+ //if (this.seenSerializedTypes.Add(type))
+ //{
+ // Debug.Log("Added " + type.GetNiceFullName());
+ //}
+
+ this.seenSerializedTypes.Add(type);
+
+ if (type.IsGenericType)
+ {
+ foreach (var arg in type.GetGenericArguments())
+ {
+ this.RegisterType(arg);
+ }
+ }
+ }
+
+ private static bool DisplaySmartUpdatingCancellableProgressBar(string title, string details, float progress, int updateIntervalByMS = 200, int updateIntervalByCall = 50)
+ {
+ bool updateProgressBar =
+ smartProgressBarWatch.ElapsedMilliseconds >= updateIntervalByMS
+ || ++smartProgressBarDisplaysSinceLastUpdate >= updateIntervalByCall;
+
+ if (updateProgressBar)
+ {
+ smartProgressBarWatch.Stop();
+ smartProgressBarWatch.Reset();
+ smartProgressBarWatch.Start();
+
+ smartProgressBarDisplaysSinceLastUpdate = 0;
+
+ if (EditorUtility.DisplayCancelableProgressBar(title, details, progress))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
+
+#endif \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta
new file mode 100644
index 00000000..5a975681
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 94a6cc2044fcd2cb317b1cdb1e8fcdaf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs
new file mode 100644
index 00000000..4f02012f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs
@@ -0,0 +1,400 @@
+//-----------------------------------------------------------------------
+// <copyright file="AOTSupportUtilities.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+#if UNITY_EDITOR
+
+namespace VRC.Udon.Serialization.OdinSerializer.Editor
+{
+ using VRC.Udon.Serialization.OdinSerializer.Utilities;
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Reflection.Emit;
+ using UnityEditor;
+ using UnityEditor.SceneManagement;
+ using UnityEngine;
+ using UnityEngine.Scripting;
+
+ public static class AOTSupportUtilities
+ {
+ /// <summary>
+ /// Scans the project's build scenes and resources, plus their dependencies, for serialized types to support. Progress bars are shown during the scan.
+ /// </summary>
+ /// <param name="serializedTypes">The serialized types to support.</param>
+ /// <param name="scanBuildScenes">Whether to scan the project's build scenes.</param>
+ /// <param name="scanAllAssetBundles">Whether to scan all the project's asset bundles.</param>
+ /// <param name="scanPreloadedAssets">Whether to scan the project's preloaded assets.</param>
+ /// <param name="scanResources">Whether to scan the project's resources.</param>
+ /// <param name="resourcesToScan">An optional list of the resource paths to scan. Only has an effect if the scanResources argument is true. All the resources will be scanned if null.</param>
+ /// <returns>true if the scan succeeded, false if the scan failed or was cancelled</returns>
+ public static bool ScanProjectForSerializedTypes(out List<Type> serializedTypes, bool scanBuildScenes = true, bool scanAllAssetBundles = true, bool scanPreloadedAssets = true, bool scanResources = true, List<string> resourcesToScan = null, bool scanAddressables = true)
+ {
+ using (var scanner = new AOTSupportScanner())
+ {
+ scanner.BeginScan();
+
+ if (scanBuildScenes && !scanner.ScanBuildScenes(includeSceneDependencies: true, showProgressBar: true))
+ {
+ Debug.Log("Project scan canceled while scanning scenes and their dependencies.");
+ serializedTypes = null;
+ return false;
+ }
+
+ if (scanResources && !scanner.ScanAllResources(includeResourceDependencies: true, showProgressBar: true, resourcesPaths: resourcesToScan))
+ {
+ Debug.Log("Project scan canceled while scanning resources and their dependencies.");
+ serializedTypes = null;
+ return false;
+ }
+
+ if (scanAllAssetBundles && !scanner.ScanAllAssetBundles(showProgressBar: true))
+ {
+ Debug.Log("Project scan canceled while scanning asset bundles and their dependencies.");
+ serializedTypes = null;
+ return false;
+ }
+
+ if (scanPreloadedAssets && !scanner.ScanPreloadedAssets(showProgressBar: true))
+ {
+ Debug.Log("Project scan canceled while scanning preloaded assets and their dependencies.");
+ serializedTypes = null;
+ return false;
+ }
+
+ if (scanAddressables && !scanner.ScanAllAddressables(includeAssetDependencies: true, showProgressBar: true))
+ {
+ Debug.Log("Project scan canceled while scanning addressable assets and their dependencies.");
+ serializedTypes = null;
+ return false;
+ }
+
+ serializedTypes = scanner.EndScan();
+ }
+ return true;
+ }
+
+ /// <summary>
+ /// Generates an AOT DLL, using the given parameters.
+ /// </summary>
+ public static void GenerateDLL(string dirPath, string assemblyName, List<Type> supportSerializedTypes, bool generateLinkXml = true)
+ {
+ #if UNITY_EDITOR && NET_4_6
+ if (!dirPath.EndsWith("/")) dirPath += "/";
+
+ var newDllPath = dirPath + assemblyName;
+ var fullDllPath = newDllPath + ".dll";
+
+ var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName() { Name = assemblyName }, AssemblyBuilderAccess.Save, dirPath);
+ var module = assembly.DefineDynamicModule(assemblyName);
+
+ assembly.SetCustomAttribute(new CustomAttributeBuilder(typeof(EmittedAssemblyAttribute).GetConstructor(new Type[0]), new object[0]));
+
+ // VRChat Edit: Add the UnityAPICompatibilityVersion Attribute for the current version of Unity to skip API Updating.
+ #if UNITY_2019
+ assembly.SetCustomAttribute(new CustomAttributeBuilder(
+ typeof(UnityAPICompatibilityVersionAttribute).GetConstructor(new[]{typeof(string), typeof(bool)}),
+ new object[]{Application.unityVersion, true})
+ );
+ #else
+ assembly.SetCustomAttribute(new CustomAttributeBuilder(
+ typeof(UnityAPICompatibilityVersionAttribute).GetConstructor(new[]{typeof(string)}),
+ new object[]{Application.unityVersion})
+ );
+ #endif
+
+ // The following is a fix for Unity's crappy Mono runtime that doesn't know how to do this sort
+ // of stuff properly
+ //
+ // We must manually remove the "Default Dynamic Assembly" module that is automatically defined,
+ // otherwise a reference to that non-existent module will be saved into the assembly's IL, and
+ // that will cause a multitude of issues.
+ //
+ // We do this by forcing there to be only one module - the one we just defined, and we set the
+ // manifest module to be that module as well.
+ {
+ var modulesField = assembly.GetType().GetField("modules", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ var manifestModuleField = assembly.GetType().GetField("manifest_module", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+ if (modulesField != null)
+ {
+ modulesField.SetValue(assembly, new ModuleBuilder[] { module });
+ }
+
+ if (manifestModuleField != null)
+ {
+ manifestModuleField.SetValue(assembly, module);
+ }
+ }
+
+ var type = module.DefineType(assemblyName + ".PreventCodeStrippingViaReferences", TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.NotPublic);
+
+ CustomAttributeBuilder attributeBuilder = new CustomAttributeBuilder(typeof(PreserveAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
+ type.SetCustomAttribute(attributeBuilder);
+
+ var staticConstructor = type.DefineTypeInitializer();
+ var il = staticConstructor.GetILGenerator();
+
+ var falseLocal = il.DeclareLocal(typeof(bool));
+
+ il.Emit(OpCodes.Ldc_I4_0); // Load false
+ il.Emit(OpCodes.Stloc, falseLocal); // Set to falseLocal
+
+ HashSet<Type> seenTypes = new HashSet<Type>();
+
+ if (UnityVersion.Major == 2019 && UnityVersion.Minor == 2)
+ {
+ // This here is a hack that fixes Unity's assembly updater triggering faultily in Unity 2019.2
+ // (and in early 2019.3 alphas/betas, but we're not handling those). When it triggers, it edits
+ // the generated AOT assembly such that it immediately causes Unity to hard crash. Having this
+ // type reference present in the assembly prevents that from happening. (Any concrete type in
+ // the serialization assembly would work, this one is just a random pick.)
+ //
+ // Unity should have fixed this in 2019.3, but said that a backport to 2019.2 is not guaranteed
+ // to occur, though it might.
+
+ supportSerializedTypes.Add(typeof(DateTimeFormatter));
+ }
+
+ //var endPoint = il.DefineLabel();
+ //il.Emit(OpCodes.Br, endPoint);
+
+ foreach (var serializedType in supportSerializedTypes)
+ {
+ if (serializedType == null) continue;
+
+ bool isAbstract = serializedType.IsAbstract || serializedType.IsInterface;
+
+ if (serializedType.IsGenericType && (serializedType.IsGenericTypeDefinition || !serializedType.IsFullyConstructedGenericType()))
+ {
+ Debug.LogError("Skipping type '" + serializedType.GetNiceFullName() + "'! Type is a generic type definition, or its arguments contain generic parameters; type must be a fully constructed generic type.");
+ continue;
+ }
+
+ if (seenTypes.Contains(serializedType)) continue;
+
+ seenTypes.Add(serializedType);
+
+ // Reference serialized type
+ {
+ if (serializedType.IsValueType)
+ {
+ var local = il.DeclareLocal(serializedType);
+
+ il.Emit(OpCodes.Ldloca, local);
+ il.Emit(OpCodes.Initobj, serializedType);
+ }
+ else if (!isAbstract)
+ {
+ var constructor = serializedType.GetConstructor(Type.EmptyTypes);
+
+ if (constructor != null)
+ {
+ il.Emit(OpCodes.Newobj, constructor);
+ il.Emit(OpCodes.Pop);
+ }
+ }
+ }
+
+ // Reference and/or create formatter type
+ if (!FormatterUtilities.IsPrimitiveType(serializedType) && !typeof(UnityEngine.Object).IsAssignableFrom(serializedType) && !isAbstract)
+ {
+ var actualFormatter = FormatterLocator.GetFormatter(serializedType, SerializationPolicies.Unity);
+
+ if (actualFormatter.GetType().IsDefined<EmittedFormatterAttribute>())
+ {
+ //TODO: Make emitted formatter code compatible with IL2CPP
+ //// Emit an actual AOT formatter into the generated assembly
+
+ //if (this.emitAOTFormatters)
+ //{
+ // var emittedFormatter = FormatterEmitter.EmitAOTFormatter(typeEntry.Type, module, SerializationPolicies.Unity);
+ // var emittedFormatterConstructor = emittedFormatter.GetConstructor(Type.EmptyTypes);
+
+ // il.Emit(OpCodes.Newobj, emittedFormatterConstructor);
+ // il.Emit(OpCodes.Pop);
+ //}
+ }
+
+ var formatters = FormatterLocator.GetAllCompatiblePredefinedFormatters(serializedType, SerializationPolicies.Unity);
+
+ foreach (var formatter in formatters)
+ {
+ // Reference the pre-existing formatter
+
+ var formatterConstructor = formatter.GetType().GetConstructor(Type.EmptyTypes);
+
+ if (formatterConstructor != null)
+ {
+ il.Emit(OpCodes.Newobj, formatterConstructor);
+ il.Emit(OpCodes.Pop);
+ }
+ }
+
+ //// Make sure we have a proper reflection formatter variant if all else goes wrong
+ //il.Emit(OpCodes.Newobj, typeof(ReflectionFormatter<>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes));
+ //il.Emit(OpCodes.Pop);
+ }
+
+ ConstructorInfo serializerConstructor;
+
+ // Reference serializer variant
+ if (serializedType.IsValueType)
+ {
+ serializerConstructor = Serializer.Get(serializedType).GetType().GetConstructor(Type.EmptyTypes);
+
+ il.Emit(OpCodes.Newobj, serializerConstructor);
+
+ // The following section is a fix for an issue on IL2CPP for PS4, where sometimes bytecode isn't
+ // generated for methods in base types of needed types - FX, Serializer<T>.ReadValueWeak()
+ // may be missing. This only seems to happen in a relevant way for value types.
+ {
+ var endLabel = il.DefineLabel();
+
+ // Load a false local value, then jump to the end of this segment of code due to that
+ // false value. This is an attempt to trick any potential code flow analysis made
+ // by IL2CPP that checks whether this segment of code is actually run.
+ //
+ // We don't run the code because if we did, that would actually throw a bunch of
+ // exceptions from invalid calls to ReadValueWeak and WriteValueWeak.
+ il.Emit(OpCodes.Ldloc, falseLocal);
+ il.Emit(OpCodes.Brfalse, endLabel);
+
+ var baseSerializerType = typeof(Serializer<>).MakeGenericType(serializedType);
+
+ var readValueWeakMethod = baseSerializerType.GetMethod("ReadValueWeak", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, new Type[] { typeof(IDataReader) }, null);
+ var writeValueWeakMethod = baseSerializerType.GetMethod("WriteValueWeak", BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly, null, new Type[] { typeof(string), typeof(object), typeof(IDataWriter) }, null);
+
+ il.Emit(OpCodes.Dup); // Duplicate serializer instance
+ il.Emit(OpCodes.Ldnull); // Load null argument for IDataReader reader
+ il.Emit(OpCodes.Callvirt, readValueWeakMethod); // Call ReadValueWeak on serializer instance
+ il.Emit(OpCodes.Pop); // Pop result of ReadValueWeak
+
+ il.Emit(OpCodes.Dup); // Duplicate serializer instance
+ il.Emit(OpCodes.Ldnull); // Load null argument for string name
+ il.Emit(OpCodes.Ldnull); // Load null argument for object value
+ il.Emit(OpCodes.Ldnull); // Load null argument for IDataWriter writer
+ il.Emit(OpCodes.Callvirt, writeValueWeakMethod); // Call WriteValueWeak on serializer instance
+
+ il.MarkLabel(endLabel); // This is where the code always jumps to, skipping the above
+ }
+
+ il.Emit(OpCodes.Pop); // Pop the serializer instance
+ }
+ else
+ {
+ serializerConstructor = typeof(ComplexTypeSerializer<>).MakeGenericType(serializedType).GetConstructor(Type.EmptyTypes);
+ il.Emit(OpCodes.Newobj, serializerConstructor);
+ il.Emit(OpCodes.Pop);
+ }
+
+ }
+
+ //il.MarkLabel(endPoint);
+ il.Emit(OpCodes.Ret);
+
+ type.CreateType();
+
+ if (!Directory.Exists(dirPath))
+ {
+ Directory.CreateDirectory(dirPath);
+ }
+
+ if (File.Exists(fullDllPath))
+ {
+ File.Delete(fullDllPath);
+ }
+
+ if (File.Exists(fullDllPath + ".meta"))
+ {
+ File.Delete(fullDllPath + ".meta");
+ }
+
+ try
+ {
+ AssetDatabase.Refresh();
+ }
+ catch (Exception)
+ {
+ // Sigh, Unity 5.3.0
+ }
+
+ assembly.Save(assemblyName);
+
+ File.Move(newDllPath, fullDllPath);
+
+ if (generateLinkXml)
+ {
+ File.WriteAllText(dirPath + "link.xml",
+ @"<linker>
+ <assembly fullname=""" + assemblyName + @""" preserve=""all""/>
+</linker>");
+ }
+
+ try
+ {
+ AssetDatabase.Refresh();
+ }
+ catch (Exception)
+ {
+ // Sigh, Unity 5.3.0
+ }
+
+ var pluginImporter = PluginImporter.GetAtPath(fullDllPath) as PluginImporter;
+
+ if (pluginImporter != null)
+ {
+ //pluginImporter.ClearSettings();
+
+ pluginImporter.SetCompatibleWithEditor(false);
+ pluginImporter.SetCompatibleWithAnyPlatform(true);
+
+ // Disable for all standalones
+ pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneLinux64, false);
+
+ if (!UnityVersion.IsVersionOrGreater(2019, 2))
+ {
+ pluginImporter.SetCompatibleWithPlatform((BuildTarget)17, false); // StandaloneLinux
+ pluginImporter.SetCompatibleWithPlatform((BuildTarget)25, false); // StandaloneLinuxUniversal
+ }
+
+ // StandaloneOSXUniversal (<= 2017.2) / StandaloneOSX (>= 2017.3)
+ pluginImporter.SetCompatibleWithPlatform((BuildTarget)2, false);
+
+ if (!UnityVersion.IsVersionOrGreater(2017, 3))
+ {
+ pluginImporter.SetCompatibleWithPlatform((BuildTarget)4, false); // StandaloneOSXIntel
+ pluginImporter.SetCompatibleWithPlatform((BuildTarget)27, false); // StandaloneOSXIntel64
+ }
+
+ pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows, false);
+ //pluginImporter.SetCompatibleWithPlatform(BuildTarget.StandaloneWindows64, false);
+
+ //pluginImporter.SetCompatibleWithPlatform(BuildTarget.Android, false);
+
+ pluginImporter.SaveAndReimport();
+ }
+
+ AssetDatabase.SaveAssets();
+ #endif
+ }
+ }
+}
+
+#endif
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta
new file mode 100644
index 00000000..0fa39b53
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f5fe153775edbadfa2b659e0e35dc881
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport.meta
new file mode 100644
index 00000000..00b643bb
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 766e16676b7508647a3ab84d0cae8fa7
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs
new file mode 100644
index 00000000..f6d1e678
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs
@@ -0,0 +1,70 @@
+//-----------------------------------------------------------------------
+// <copyright file="BaseDictionaryKeyPathProvider.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Collections.Generic;
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ /// <typeparam name="T">Not yet documented.</typeparam>
+ public abstract class BaseDictionaryKeyPathProvider<T> : IDictionaryKeyPathProvider<T>, IComparer<T>
+ {
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public abstract string ProviderID { get; }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ /// <param name="pathStr">Not yet documented.</param>
+ /// <returns>Not yet documented.</returns>
+ public abstract T GetKeyFromPathString(string pathStr);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ /// <param name="key">Not yet documented.</param>
+ /// <returns>Not yet documented.</returns>
+ public abstract string GetPathStringFromKey(T key);
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ /// <param name="x">Not yet documented.</param>
+ /// <param name="y">Not yet documented.</param>
+ /// <returns>Not yet documented.</returns>
+ public abstract int Compare(T x, T y);
+
+ int IDictionaryKeyPathProvider.Compare(object x, object y)
+ {
+ return this.Compare((T)x, (T)y);
+ }
+
+ object IDictionaryKeyPathProvider.GetKeyFromPathString(string pathStr)
+ {
+ return this.GetKeyFromPathString(pathStr);
+ }
+
+ string IDictionaryKeyPathProvider.GetPathStringFromKey(object key)
+ {
+ return this.GetPathStringFromKey((T)key);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta
new file mode 100644
index 00000000..9e6fcbb6
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 864fb1c011715f9df2998d71ac8716f6
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs
new file mode 100644
index 00000000..78dbb94c
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs
@@ -0,0 +1,467 @@
+//-----------------------------------------------------------------------
+// <copyright file="DictionaryKeyUtility.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Globalization;
+ using System;
+ using System.Collections.Generic;
+ using Utilities;
+ using System.Linq;
+ using UnityEngine;
+ using System.Reflection;
+
+ /// <summary>
+ /// Provides utility methods for handling dictionary keys in the prefab modification system.
+ /// </summary>
+ public static class DictionaryKeyUtility
+ {
+ private static readonly Dictionary<Type, bool> GetSupportedDictionaryKeyTypesResults = new Dictionary<Type, bool>();
+
+ private static readonly HashSet<Type> BaseSupportedDictionaryKeyTypes = new HashSet<Type>()
+ {
+ typeof(string),
+ typeof(char),
+ typeof(byte),
+ typeof(sbyte),
+ typeof(ushort),
+ typeof(short),
+ typeof(uint),
+ typeof(int),
+ typeof(ulong),
+ typeof(long),
+ typeof(float),
+ typeof(double),
+ typeof(decimal),
+ typeof(Guid)
+ };
+
+ private static readonly HashSet<char> AllowedSpecialKeyStrChars = new HashSet<char>()
+ {
+ ',', '(', ')', '\\', '|', '-', '+'
+ };
+
+ private static readonly Dictionary<Type, IDictionaryKeyPathProvider> TypeToKeyPathProviders = new Dictionary<Type, IDictionaryKeyPathProvider>();
+ private static readonly Dictionary<string, IDictionaryKeyPathProvider> IDToKeyPathProviders = new Dictionary<string, IDictionaryKeyPathProvider>();
+ private static readonly Dictionary<IDictionaryKeyPathProvider, string> ProviderToID = new Dictionary<IDictionaryKeyPathProvider, string>();
+
+ private static readonly Dictionary<object, string> ObjectsToTempKeys = new Dictionary<object, string>();
+ private static readonly Dictionary<string, object> TempKeysToObjects = new Dictionary<string, object>();
+ private static long tempKeyCounter = 0;
+
+ private class UnityObjectKeyComparer<T> : IComparer<T>
+ {
+ public int Compare(T x, T y)
+ {
+ var a = (UnityEngine.Object)(object)x;
+ var b = (UnityEngine.Object)(object)y;
+
+ if (a == null && b == null) return 0;
+
+ if (a == null) return 1;
+ if (b == null) return -1;
+
+ return a.name.CompareTo(b.name);
+ }
+ }
+
+ private class FallbackKeyComparer<T> : IComparer<T>
+ {
+ public int Compare(T x, T y)
+ {
+ return GetDictionaryKeyString(x).CompareTo(GetDictionaryKeyString(y));
+ }
+ }
+
+ /// <summary>
+ /// A smart comparer for dictionary keys, that uses the most appropriate available comparison method for the given key types.
+ /// </summary>
+ public class KeyComparer<T> : IComparer<T>
+ {
+ public readonly static KeyComparer<T> Default = new KeyComparer<T>();
+
+ private readonly IComparer<T> actualComparer;
+
+ public KeyComparer()
+ {
+ IDictionaryKeyPathProvider provider;
+
+ if (TypeToKeyPathProviders.TryGetValue(typeof(T), out provider))
+ {
+ this.actualComparer = (IComparer<T>)provider;
+ }
+ else if (typeof(IComparable).IsAssignableFrom(typeof(T)) || typeof(IComparable<T>).IsAssignableFrom(typeof(T)))
+ {
+ this.actualComparer = Comparer<T>.Default;
+ }
+ else if (typeof(UnityEngine.Object).IsAssignableFrom(typeof(T)))
+ {
+ this.actualComparer = new UnityObjectKeyComparer<T>();
+ }
+ else
+ {
+ this.actualComparer = new FallbackKeyComparer<T>();
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ /// <param name="x">Not yet documented.</param>
+ /// <param name="y">Not yet documented.</param>
+ /// <returns>Not yet documented.</returns>
+ public int Compare(T x, T y)
+ {
+ return this.actualComparer.Compare(x, y);
+ }
+ }
+
+ static DictionaryKeyUtility()
+ {
+ var attributes = AppDomain.CurrentDomain.GetAssemblies()
+ .SelectMany(ass =>
+ {
+ return ass.SafeGetCustomAttributes(typeof(RegisterDictionaryKeyPathProviderAttribute), false)
+ .Select(attr => new { Assembly = ass, Attribute = (RegisterDictionaryKeyPathProviderAttribute)attr });
+ })
+ .Where(n => n.Attribute.ProviderType != null);
+
+ foreach (var entry in attributes)
+ {
+ var assembly = entry.Assembly;
+ var providerType = entry.Attribute.ProviderType;
+
+ if (providerType.IsAbstract)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Type cannot be abstract");
+ continue;
+ }
+
+ if (providerType.IsInterface)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Type cannot be an interface");
+ continue;
+ }
+
+ if (!providerType.ImplementsOpenGenericInterface(typeof(IDictionaryKeyPathProvider<>)))
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Type must implement the " + typeof(IDictionaryKeyPathProvider<>).GetNiceName() + " interface");
+ continue;
+ }
+
+ if (providerType.IsGenericType)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Type cannot be generic");
+ continue;
+ }
+
+ if (providerType.GetConstructor(Type.EmptyTypes) == null)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Type must have a public parameterless constructor");
+ continue;
+ }
+
+ var keyType = providerType.GetArgumentsOfInheritedOpenGenericInterface(typeof(IDictionaryKeyPathProvider<>))[0];
+
+ if (!keyType.IsValueType)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Key type to support '" + keyType.GetNiceFullName() + "' must be a value type - support for extending dictionaries with reference type keys may come at a later time");
+ continue;
+ }
+
+ if (TypeToKeyPathProviders.ContainsKey(keyType))
+ {
+ Debug.LogWarning("Ignoring dictionary key path provider '" + providerType.GetNiceFullName() + "' registered on assembly '" + assembly.GetName().Name + "': A previous provider '" + TypeToKeyPathProviders[keyType].GetType().GetNiceFullName() + "' was already registered for the key type '" + keyType.GetNiceFullName() + "'.");
+ continue;
+ }
+
+ IDictionaryKeyPathProvider provider;
+ string id;
+
+ try
+ {
+ provider = (IDictionaryKeyPathProvider)Activator.CreateInstance(providerType);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(ex);
+ Debug.LogWarning("Ignoring dictionary key path provider '" + providerType.GetNiceFullName() + "' registered on assembly '" + assembly.GetName().Name + "': An exception of type '" + ex.GetType() + "' was thrown when trying to instantiate a provider instance.");
+ continue;
+ }
+
+ try
+ {
+ id = provider.ProviderID;
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(ex);
+ Debug.LogWarning("Ignoring dictionary key path provider '" + providerType.GetNiceFullName() + "' registered on assembly '" + assembly.GetName().Name + "': An exception of type '" + ex.GetType() + "' was thrown when trying to get the provider ID string.");
+ continue;
+ }
+
+ if (id == null)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Provider ID is null");
+ continue;
+ }
+
+ if (id.Length == 0)
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Provider ID is an empty string");
+ continue;
+ }
+
+ for (int i = 0; i < id.Length; i++)
+ {
+ if (!char.IsLetterOrDigit(id[i]))
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Provider ID '" + id + "' cannot contain characters which are not letters or digits");
+ continue;
+ }
+ }
+
+ if (IDToKeyPathProviders.ContainsKey(id))
+ {
+ LogInvalidKeyPathProvider(providerType, assembly, "Provider ID '" + id + "' is already in use for the provider '" + IDToKeyPathProviders[id].GetType().GetNiceFullName() + "'");
+ continue;
+ }
+
+ TypeToKeyPathProviders[keyType] = provider;
+ IDToKeyPathProviders[id] = provider;
+ ProviderToID[provider] = id;
+ }
+ }
+
+ private static void LogInvalidKeyPathProvider(Type type, Assembly assembly, string reason)
+ {
+ Debug.LogError("Invalid dictionary key path provider '" + type.GetNiceFullName() + "' registered on assembly '" + assembly.GetName().Name + "': " + reason);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static IEnumerable<Type> GetPersistentPathKeyTypes()
+ {
+ foreach (var type in BaseSupportedDictionaryKeyTypes)
+ {
+ yield return type;
+ }
+
+ foreach (var type in TypeToKeyPathProviders.Keys)
+ {
+ yield return type;
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static bool KeyTypeSupportsPersistentPaths(Type type)
+ {
+ bool result;
+ if (!GetSupportedDictionaryKeyTypesResults.TryGetValue(type, out result))
+ {
+ result = PrivateIsSupportedDictionaryKeyType(type);
+ GetSupportedDictionaryKeyTypesResults.Add(type, result);
+ }
+ return result;
+ }
+
+ private static bool PrivateIsSupportedDictionaryKeyType(Type type)
+ {
+ return type.IsEnum || BaseSupportedDictionaryKeyTypes.Contains(type) || TypeToKeyPathProviders.ContainsKey(type);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static string GetDictionaryKeyString(object key)
+ {
+ if (key == null)
+ {
+ throw new ArgumentNullException("key");
+ }
+
+ Type type = key.GetType();
+
+ if (!KeyTypeSupportsPersistentPaths(type))
+ {
+ string keyString;
+
+ if (!ObjectsToTempKeys.TryGetValue(key, out keyString))
+ {
+ keyString = (tempKeyCounter++).ToString();
+ var str = "{temp:" + keyString + "}";
+ ObjectsToTempKeys[key] = str;
+ TempKeysToObjects[str] = key;
+ }
+
+ return keyString;
+ }
+
+ IDictionaryKeyPathProvider keyPathProvider;
+
+ if (TypeToKeyPathProviders.TryGetValue(type, out keyPathProvider))
+ {
+ var keyStr = keyPathProvider.GetPathStringFromKey(key);
+ string error = null;
+
+ bool validPath = true;
+
+ if (keyStr == null || keyStr.Length == 0)
+ {
+ validPath = false;
+ error = "String is null or empty";
+ }
+
+ if (validPath)
+ {
+ for (int i = 0; i < keyStr.Length; i++)
+ {
+ var c = keyStr[i];
+
+ if (char.IsLetterOrDigit(c) || AllowedSpecialKeyStrChars.Contains(c)) continue;
+
+ validPath = false;
+ error = "Invalid character '" + c + "' at index " + i;
+ break;
+ }
+ }
+
+ if (!validPath)
+ {
+ throw new ArgumentException("Invalid key path '" + keyStr + "' given by provider '" + keyPathProvider.GetType().GetNiceFullName() + "': " + error);
+ }
+
+ return "{id:" + ProviderToID[keyPathProvider] + ":" + keyStr + "}";
+ }
+
+ if (type.IsEnum)
+ {
+ Type backingType = Enum.GetUnderlyingType(type);
+
+ if (backingType == typeof(ulong))
+ {
+ ulong value = Convert.ToUInt64(key);
+ return "{" + value.ToString("D", CultureInfo.InvariantCulture) + "eu}";
+ }
+ else
+ {
+ long value = Convert.ToInt64(key);
+ return "{" + value.ToString("D", CultureInfo.InvariantCulture) + "es}";
+ }
+ }
+
+ if (type == typeof(string)) return "{\"" + key + "\"}";
+ if (type == typeof(char)) return "{'" + ((char)key).ToString(CultureInfo.InvariantCulture) + "'}";
+ if (type == typeof(byte)) return "{" + ((byte)key).ToString("D", CultureInfo.InvariantCulture) + "ub}";
+ if (type == typeof(sbyte)) return "{" + ((sbyte)key).ToString("D", CultureInfo.InvariantCulture) + "sb}";
+ if (type == typeof(ushort)) return "{" + ((ushort)key).ToString("D", CultureInfo.InvariantCulture) + "us}";
+ if (type == typeof(short)) return "{" + ((short)key).ToString("D", CultureInfo.InvariantCulture) + "ss}";
+ if (type == typeof(uint)) return "{" + ((uint)key).ToString("D", CultureInfo.InvariantCulture) + "ui}";
+ if (type == typeof(int)) return "{" + ((int)key).ToString("D", CultureInfo.InvariantCulture) + "si}";
+ if (type == typeof(ulong)) return "{" + ((ulong)key).ToString("D", CultureInfo.InvariantCulture) + "ul}";
+ if (type == typeof(long)) return "{" + ((long)key).ToString("D", CultureInfo.InvariantCulture) + "sl}";
+ if (type == typeof(float)) return "{" + ((float)key).ToString("R", CultureInfo.InvariantCulture) + "fl}";
+ if (type == typeof(double)) return "{" + ((double)key).ToString("R", CultureInfo.InvariantCulture) + "dl}";
+ if (type == typeof(decimal)) return "{" + ((decimal)key).ToString("G", CultureInfo.InvariantCulture) + "dc}";
+ if (type == typeof(Guid)) return "{" + ((Guid)key).ToString("N", CultureInfo.InvariantCulture) + "gu}";
+
+ throw new NotImplementedException("Support has not been implemented for the supported dictionary key type '" + type.GetNiceName() + "'.");
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static object GetDictionaryKeyValue(string keyStr, Type expectedType)
+ {
+ const string InvalidKeyString = "Invalid key string: ";
+
+ if (keyStr == null) throw new ArgumentNullException("keyStr");
+ if (keyStr.Length < 4 || keyStr[0] != '{' || keyStr[keyStr.Length - 1] != '}') throw new ArgumentException(InvalidKeyString + keyStr);
+
+ if (keyStr[1] == '"')
+ {
+ if (keyStr[keyStr.Length - 2] != '"') throw new ArgumentException(InvalidKeyString + keyStr);
+ return keyStr.Substring(2, keyStr.Length - 4);
+ }
+
+ if (keyStr[1] == '\'')
+ {
+ if (keyStr.Length != 5 || keyStr[keyStr.Length - 2] != '\'') throw new ArgumentException(InvalidKeyString + keyStr);
+ return keyStr[2];
+ }
+
+ if (keyStr.StartsWith("{temp:"))
+ {
+ object key;
+
+ if (!TempKeysToObjects.TryGetValue(keyStr, out key))
+ {
+ throw new ArgumentException("The temp dictionary key '" + keyStr + "' has not been allocated yet.");
+ }
+
+ return key;
+ }
+
+ if (keyStr.StartsWith("{id:"))
+ {
+ int secondColon = keyStr.IndexOf(':', 4);
+
+ if (secondColon == -1 || secondColon > keyStr.Length - 3) throw new ArgumentException(InvalidKeyString + keyStr);
+
+ string id = keyStr.FromTo(4, secondColon);
+ string key = keyStr.FromTo(secondColon + 1, keyStr.Length - 1);
+
+ IDictionaryKeyPathProvider provider;
+
+ if (!IDToKeyPathProviders.TryGetValue(id, out provider))
+ {
+ throw new ArgumentException("No provider found for provider ID '" + id + "' in key string '" + keyStr + "'.");
+ }
+
+ return provider.GetKeyFromPathString(key);
+ }
+
+ // Handle enums
+
+ if (keyStr.EndsWith("ub}")) return byte.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("sb}")) return sbyte.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("us}")) return ushort.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("ss}")) return short.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("ui}")) return uint.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("si}")) return int.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("ul}")) return ulong.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("sl}")) return long.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("fl}")) return float.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("dl}")) return double.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("dc}")) return decimal.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any);
+ if (keyStr.EndsWith("gu}")) return new Guid(keyStr.Substring(1, keyStr.Length - 4));
+ if (keyStr.EndsWith("es}")) return Enum.ToObject(expectedType, long.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any));
+ if (keyStr.EndsWith("eu}")) return Enum.ToObject(expectedType, ulong.Parse(keyStr.Substring(1, keyStr.Length - 4), NumberStyles.Any));
+
+ throw new ArgumentException(InvalidKeyString + keyStr);
+ }
+
+ private static string FromTo(this string str, int from, int to)
+ {
+ return str.Substring(from, to - from);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta
new file mode 100644
index 00000000..fcf54e24
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ef6f699f176c2dfdea788982526f989a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs
new file mode 100644
index 00000000..ae9ab609
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs
@@ -0,0 +1,75 @@
+//-----------------------------------------------------------------------
+// <copyright file="IDictionaryKeyPathProvider.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public interface IDictionaryKeyPathProvider
+ {
+ /// <summary>
+ /// Gets the provider identifier.
+ /// </summary>
+ string ProviderID { get; }
+
+ /// <summary>
+ /// Gets the path string from key.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ string GetPathStringFromKey(object key);
+
+ /// <summary>
+ /// Gets the key from path string.
+ /// </summary>
+ /// <param name="pathStr">The path string.</param>
+ object GetKeyFromPathString(string pathStr);
+
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ int Compare(object x, object y);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public interface IDictionaryKeyPathProvider<T> : IDictionaryKeyPathProvider
+ {
+ /// <summary>
+ /// Gets the path string from key.
+ /// </summary>
+ /// <param name="key">The key.</param>
+ string GetPathStringFromKey(T key);
+
+ /// <summary>
+ /// Gets the key from path string.
+ /// </summary>
+ /// <param name="pathStr">The path string.</param>
+ new T GetKeyFromPathString(string pathStr);
+
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ int Compare(T x, T y);
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta
new file mode 100644
index 00000000..dcb5d6eb
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b513c156933d8b833ccd40d717bf7e2b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs
new file mode 100644
index 00000000..c3b04f6a
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="RegisterDictionaryKeyPathProviderAttribute.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+
+ [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
+ public sealed class RegisterDictionaryKeyPathProviderAttribute : Attribute
+ {
+ public readonly Type ProviderType;
+
+ public RegisterDictionaryKeyPathProviderAttribute(Type providerType)
+ {
+ this.ProviderType = providerType;
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta
new file mode 100644
index 00000000..adbddde2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 54f653ed4a4e15c07057283c11dce4d9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs
new file mode 100644
index 00000000..972edfdd
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs
@@ -0,0 +1,76 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector2DictionaryKeyPathProvider.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterDictionaryKeyPathProvider(typeof(Vector2DictionaryKeyPathProvider))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Globalization;
+ using UnityEngine;
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public sealed class Vector2DictionaryKeyPathProvider : BaseDictionaryKeyPathProvider<Vector2>
+ {
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public override string ProviderID { get { return "v2"; } }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public override int Compare(Vector2 x, Vector2 y)
+ {
+ int result = x.x.CompareTo(y.x);
+
+ if (result == 0)
+ {
+ result = x.y.CompareTo(y.y);
+ }
+
+ return result;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public override Vector2 GetKeyFromPathString(string pathStr)
+ {
+ int sep = pathStr.IndexOf('|');
+
+ string x = pathStr.Substring(1, sep - 1).Trim();
+ string y = pathStr.Substring(sep + 1, pathStr.Length - (sep + 2)).Trim();
+
+ return new Vector2(float.Parse(x), float.Parse(y));
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public override string GetPathStringFromKey(Vector2 key)
+ {
+ var x = key.x.ToString("R", CultureInfo.InvariantCulture);
+ var y = key.y.ToString("R", CultureInfo.InvariantCulture);
+ return ("(" + x + "|" + y + ")").Replace('.', ',');
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta
new file mode 100644
index 00000000..afcdc7bc
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0405ef103432161dff609e75f71f3f55
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs
new file mode 100644
index 00000000..20616ad2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs
@@ -0,0 +1,72 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector3DictionaryKeyPathProvider.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterDictionaryKeyPathProvider(typeof(Vector3DictionaryKeyPathProvider))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Globalization;
+ using UnityEngine;
+
+ /// <summary>
+ /// Dictionary key path provider for <see cref="UnityEngine.Vector3"/>
+ /// </summary>
+ public sealed class Vector3DictionaryKeyPathProvider : BaseDictionaryKeyPathProvider<Vector3>
+ {
+ public override string ProviderID { get { return "v3"; } }
+
+ public override int Compare(Vector3 x, Vector3 y)
+ {
+ int result = x.x.CompareTo(y.x);
+
+ if (result == 0)
+ {
+ result = x.y.CompareTo(y.y);
+ }
+
+ if (result == 0)
+ {
+ result = x.z.CompareTo(y.z);
+ }
+
+ return result;
+ }
+
+ public override Vector3 GetKeyFromPathString(string pathStr)
+ {
+ int sep1 = pathStr.IndexOf('|');
+ int sep2 = pathStr.IndexOf('|', sep1 + 1);
+
+ string x = pathStr.Substring(1, sep1 - 1).Trim();
+ string y = pathStr.Substring(sep1 + 1, sep2 - (sep1 + 1)).Trim();
+ string z = pathStr.Substring(sep2 + 1, pathStr.Length - (sep2 + 2)).Trim();
+
+ return new Vector3(float.Parse(x), float.Parse(y), float.Parse(z));
+ }
+
+ public override string GetPathStringFromKey(Vector3 key)
+ {
+ var x = key.x.ToString("R", CultureInfo.InvariantCulture);
+ var y = key.y.ToString("R", CultureInfo.InvariantCulture);
+ var z = key.z.ToString("R", CultureInfo.InvariantCulture);
+ return ("(" + x + "|" + y + "|" + z + ")").Replace('.', ',');
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta
new file mode 100644
index 00000000..04a831b9
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1d61e235c606c1c9d7269f7e68471e38
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs
new file mode 100644
index 00000000..ea499259
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs
@@ -0,0 +1,78 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector4DictionaryKeyPathProvider.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterDictionaryKeyPathProvider(typeof(Vector4DictionaryKeyPathProvider))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Globalization;
+ using UnityEngine;
+
+ public sealed class Vector4DictionaryKeyPathProvider : BaseDictionaryKeyPathProvider<Vector4>
+ {
+ public override string ProviderID { get { return "v4"; } }
+
+ public override int Compare(Vector4 x, Vector4 y)
+ {
+ int result = x.x.CompareTo(y.x);
+
+ if (result == 0)
+ {
+ result = x.y.CompareTo(y.y);
+ }
+
+ if (result == 0)
+ {
+ result = x.z.CompareTo(y.z);
+ }
+
+ if (result == 0)
+ {
+ result = x.w.CompareTo(y.w);
+ }
+
+ return result;
+ }
+
+ public override Vector4 GetKeyFromPathString(string pathStr)
+ {
+ int sep1 = pathStr.IndexOf('|');
+ int sep2 = pathStr.IndexOf('|', sep1 + 1);
+ int sep3 = pathStr.IndexOf('|', sep2 + 1);
+
+ string x = pathStr.Substring(1, sep1 - 1).Trim();
+ string y = pathStr.Substring(sep1 + 1, sep2 - (sep1 + 1)).Trim();
+ string z = pathStr.Substring(sep2 + 1, sep3 - (sep2 + 1)).Trim();
+ string w = pathStr.Substring(sep3 + 1, pathStr.Length - (sep3 + 2)).Trim();
+
+ return new Vector4(float.Parse(x), float.Parse(y), float.Parse(z), float.Parse(w));
+ }
+
+ public override string GetPathStringFromKey(Vector4 key)
+ {
+ var x = key.x.ToString("R", CultureInfo.InvariantCulture);
+ var y = key.y.ToString("R", CultureInfo.InvariantCulture);
+ var z = key.z.ToString("R", CultureInfo.InvariantCulture);
+ var w = key.w.ToString("R", CultureInfo.InvariantCulture);
+
+ return ("(" + x + "|" + y + "|" + z + "|" + w + ")").Replace('.', ',');
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta
new file mode 100644
index 00000000..fb88e42b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 51bb2cf369b5ea90948a20e4f2ebae48
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters.meta
new file mode 100644
index 00000000..84864a7a
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: aee2797df5ac0f24d97dc6ce96c65ca4
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs
new file mode 100644
index 00000000..4e798c3a
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------
+// <copyright file="AnimationCurveFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(AnimationCurveFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="AnimationCurve"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.AnimationCurve}" />
+ public class AnimationCurveFormatter : MinimalBaseFormatter<AnimationCurve>
+ {
+ private static readonly Serializer<Keyframe[]> KeyframeSerializer = Serializer.Get<Keyframe[]>();
+ private static readonly Serializer<WrapMode> WrapModeSerializer = Serializer.Get<WrapMode>();
+
+ /// <summary>
+ /// Returns null.
+ /// </summary>
+ /// <returns>
+ /// A null value.
+ /// </returns>
+ protected override AnimationCurve GetUninitializedObject()
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref AnimationCurve value, IDataReader reader)
+ {
+ var keys = KeyframeSerializer.ReadValue(reader);
+
+ value = new AnimationCurve(keys);
+ value.preWrapMode = WrapModeSerializer.ReadValue(reader);
+ value.postWrapMode = WrapModeSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref AnimationCurve value, IDataWriter writer)
+ {
+ KeyframeSerializer.WriteValue(value.keys, writer);
+ WrapModeSerializer.WriteValue(value.preWrapMode, writer);
+ WrapModeSerializer.WriteValue(value.postWrapMode, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta
new file mode 100644
index 00000000..9d72bfaa
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3d2976bd61cccf62b11b4d3f02762465
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs
new file mode 100644
index 00000000..ab175f31
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="BoundsFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(BoundsFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Bounds"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Bounds}" />
+ public class BoundsFormatter : MinimalBaseFormatter<Bounds>
+ {
+ private static readonly Serializer<Vector3> Vector3Serializer = Serializer.Get<Vector3>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Bounds value, IDataReader reader)
+ {
+ value.center = Vector3Serializer.ReadValue(reader);
+ value.size = Vector3Serializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Bounds value, IDataWriter writer)
+ {
+ Vector3Serializer.WriteValue(value.center, writer);
+ Vector3Serializer.WriteValue(value.size, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta
new file mode 100644
index 00000000..39e28ff9
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6ff1b29d64402a15d020739becd8661e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs
new file mode 100644
index 00000000..42d744aa
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="Color32Formatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(Color32Formatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Color32"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Color32}" />
+ public class Color32Formatter : MinimalBaseFormatter<Color32>
+ {
+ private static readonly Serializer<byte> ByteSerializer = Serializer.Get<byte>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Color32 value, IDataReader reader)
+ {
+ value.r = Color32Formatter.ByteSerializer.ReadValue(reader);
+ value.g = Color32Formatter.ByteSerializer.ReadValue(reader);
+ value.b = Color32Formatter.ByteSerializer.ReadValue(reader);
+ value.a = Color32Formatter.ByteSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Color32 value, IDataWriter writer)
+ {
+ Color32Formatter.ByteSerializer.WriteValue(value.r, writer);
+ Color32Formatter.ByteSerializer.WriteValue(value.g, writer);
+ Color32Formatter.ByteSerializer.WriteValue(value.b, writer);
+ Color32Formatter.ByteSerializer.WriteValue(value.a, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta
new file mode 100644
index 00000000..0ede86ff
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0878ec68b6ab3c9ebc365b6d139e4840
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs
new file mode 100644
index 00000000..3528161b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs
@@ -0,0 +1,95 @@
+//-----------------------------------------------------------------------
+// <copyright file="ColorBlockFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatterLocator(typeof(ColorBlockFormatterLocator))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+ using System.Reflection;
+ using UnityEngine;
+
+ public class ColorBlockFormatterLocator : IFormatterLocator
+ {
+ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter)
+ {
+ if (step == FormatterLocationStep.BeforeRegisteredFormatters && type.FullName == "UnityEngine.UI.ColorBlock")
+ {
+ var formatterType = typeof(ColorBlockFormatter<>).MakeGenericType(type);
+ formatter = (IFormatter)Activator.CreateInstance(formatterType);
+ return true;
+ }
+
+ formatter = null;
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Custom formatter for the <see cref="ColorBlock"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.UI.ColorBlock}" />
+ public class ColorBlockFormatter<T> : MinimalBaseFormatter<T>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+ private static readonly Serializer<Color> ColorSerializer = Serializer.Get<Color>();
+
+ private static readonly PropertyInfo normalColor = typeof(T).GetProperty("normalColor");
+ private static readonly PropertyInfo highlightedColor = typeof(T).GetProperty("highlightedColor");
+ private static readonly PropertyInfo pressedColor = typeof(T).GetProperty("pressedColor");
+ private static readonly PropertyInfo disabledColor = typeof(T).GetProperty("disabledColor");
+ private static readonly PropertyInfo colorMultiplier = typeof(T).GetProperty("colorMultiplier");
+ private static readonly PropertyInfo fadeDuration = typeof(T).GetProperty("fadeDuration");
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref T value, IDataReader reader)
+ {
+ object boxed = value;
+
+ normalColor.SetValue(boxed, ColorSerializer.ReadValue(reader), null);
+ highlightedColor.SetValue(boxed, ColorSerializer.ReadValue(reader), null);
+ pressedColor.SetValue(boxed, ColorSerializer.ReadValue(reader), null);
+ disabledColor.SetValue(boxed, ColorSerializer.ReadValue(reader), null);
+ colorMultiplier.SetValue(boxed, FloatSerializer.ReadValue(reader), null);
+ fadeDuration.SetValue(boxed, FloatSerializer.ReadValue(reader), null);
+
+ value = (T)boxed;
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref T value, IDataWriter writer)
+ {
+ ColorSerializer.WriteValue((Color)normalColor.GetValue(value, null), writer);
+ ColorSerializer.WriteValue((Color)highlightedColor.GetValue(value, null), writer);
+ ColorSerializer.WriteValue((Color)pressedColor.GetValue(value, null), writer);
+ ColorSerializer.WriteValue((Color)disabledColor.GetValue(value, null), writer);
+ FloatSerializer.WriteValue((float)colorMultiplier.GetValue(value, null), writer);
+ FloatSerializer.WriteValue((float)fadeDuration.GetValue(value, null), writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta
new file mode 100644
index 00000000..c63edfd6
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 25e35581ce6d1febd9ac41864a76ecdb
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs
new file mode 100644
index 00000000..d5bd2621
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="ColorFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(ColorFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Color"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Color}" />
+ public class ColorFormatter : MinimalBaseFormatter<Color>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Color value, IDataReader reader)
+ {
+ value.r = ColorFormatter.FloatSerializer.ReadValue(reader);
+ value.g = ColorFormatter.FloatSerializer.ReadValue(reader);
+ value.b = ColorFormatter.FloatSerializer.ReadValue(reader);
+ value.a = ColorFormatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Color value, IDataWriter writer)
+ {
+ ColorFormatter.FloatSerializer.WriteValue(value.r, writer);
+ ColorFormatter.FloatSerializer.WriteValue(value.g, writer);
+ ColorFormatter.FloatSerializer.WriteValue(value.b, writer);
+ ColorFormatter.FloatSerializer.WriteValue(value.a, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta
new file mode 100644
index 00000000..4963c973
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 484768ba343a6a05522c29d81a4ce61d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs
new file mode 100644
index 00000000..7201c6b2
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs
@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------
+// <copyright file="CoroutineFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(CoroutineFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+ using UnityEngine;
+
+ /// <summary>
+ /// <para>
+ /// Custom formatter for the <see cref="Coroutine"/> type.
+ /// This serializes nothing and always deserializes null,
+ /// and only exists to ensure that no coroutine instances
+ /// are ever created by the serialization system, since they
+ /// will in almost all cases be invalid instances.
+ /// </para>
+ /// <para>
+ /// Invalid coroutine instances crash Unity instantly when
+ /// they are garbage collected.
+ /// </para>
+ /// </summary>
+ public sealed class CoroutineFormatter : IFormatter<Coroutine>
+ {
+ /// <summary>
+ /// Gets the type that the formatter can serialize.
+ /// </summary>
+ /// <value>
+ /// The type that the formatter can serialize.
+ /// </value>
+ public Type SerializedType { get { return typeof(Coroutine); } }
+
+ /// <summary>
+ /// Returns null.
+ /// </summary>
+ object IFormatter.Deserialize(IDataReader reader)
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Returns null.
+ /// </summary>
+ public Coroutine Deserialize(IDataReader reader)
+ {
+ return null;
+ }
+
+ /// <summary>
+ /// Does nothing.
+ /// </summary>
+ public void Serialize(object value, IDataWriter writer)
+ {
+ }
+
+ /// <summary>
+ /// Does nothing.
+ /// </summary>
+ public void Serialize(Coroutine value, IDataWriter writer)
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta
new file mode 100644
index 00000000..cb1282bf
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c3968bef792c5668478ac01be7645b30
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs
new file mode 100644
index 00000000..01ca811f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="GradientAlphaKeyFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(GradientAlphaKeyFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="GradientAlphaKey"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.GradientAlphaKey}" />
+ public class GradientAlphaKeyFormatter : MinimalBaseFormatter<GradientAlphaKey>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref GradientAlphaKey value, IDataReader reader)
+ {
+ value.alpha = GradientAlphaKeyFormatter.FloatSerializer.ReadValue(reader);
+ value.time = GradientAlphaKeyFormatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref GradientAlphaKey value, IDataWriter writer)
+ {
+ GradientAlphaKeyFormatter.FloatSerializer.WriteValue(value.alpha, writer);
+ GradientAlphaKeyFormatter.FloatSerializer.WriteValue(value.time, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta
new file mode 100644
index 00000000..1b0a78e1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b5b415c00da8157ac50b8f5543f0b1d9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs
new file mode 100644
index 00000000..324cbb78
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs
@@ -0,0 +1,58 @@
+//-----------------------------------------------------------------------
+// <copyright file="GradientColorKeyFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(GradientColorKeyFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="GradientColorKey"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.GradientColorKey}" />
+ public class GradientColorKeyFormatter : MinimalBaseFormatter<GradientColorKey>
+ {
+ private static readonly Serializer<Color> ColorSerializer = Serializer.Get<Color>();
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref GradientColorKey value, IDataReader reader)
+ {
+ value.color = GradientColorKeyFormatter.ColorSerializer.ReadValue(reader);
+ value.time = GradientColorKeyFormatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref GradientColorKey value, IDataWriter writer)
+ {
+ GradientColorKeyFormatter.ColorSerializer.WriteValue(value.color, writer);
+ GradientColorKeyFormatter.FloatSerializer.WriteValue(value.time, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta
new file mode 100644
index 00000000..7f99b928
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8936a3e43078251682f18923139f7aee
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs
new file mode 100644
index 00000000..e0fe9c84
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs
@@ -0,0 +1,106 @@
+//-----------------------------------------------------------------------
+// <copyright file="GradientFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(GradientFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+ using System.Reflection;
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Gradient"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Gradient}" />
+ public class GradientFormatter : MinimalBaseFormatter<Gradient>
+ {
+ private static readonly Serializer<GradientAlphaKey[]> AlphaKeysSerializer = Serializer.Get<GradientAlphaKey[]>();
+ private static readonly Serializer<GradientColorKey[]> ColorKeysSerializer = Serializer.Get<GradientColorKey[]>();
+
+ // The Gradient.mode member of type UnityEngine.GradientMode was added in a later version of Unity
+ // Therefore we need to handle it using reflection, as it might not be there if Odin is running in an early version
+
+ private static readonly PropertyInfo ModeProperty = typeof(Gradient).GetProperty("mode", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+ private static readonly Serializer<object> EnumSerializer = ModeProperty != null ? Serializer.Get<object>() : null;
+
+ protected override Gradient GetUninitializedObject()
+ {
+ return new Gradient();
+ }
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Gradient value, IDataReader reader)
+ {
+ value.alphaKeys = GradientFormatter.AlphaKeysSerializer.ReadValue(reader);
+ value.colorKeys = GradientFormatter.ColorKeysSerializer.ReadValue(reader);
+
+ string name;
+ reader.PeekEntry(out name);
+
+ if (name == "mode")
+ {
+ try
+ {
+ if (ModeProperty != null)
+ {
+ ModeProperty.SetValue(value, EnumSerializer.ReadValue(reader), null);
+ }
+ else
+ {
+ reader.SkipEntry();
+ }
+ }
+ catch (Exception)
+ {
+ reader.Context.Config.DebugContext.LogWarning("Failed to read Gradient.mode, due to Unity's API disallowing setting of this member on other threads than the main thread. Gradient.mode value will have been lost.");
+ }
+ }
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Gradient value, IDataWriter writer)
+ {
+ GradientFormatter.AlphaKeysSerializer.WriteValue(value.alphaKeys, writer);
+ GradientFormatter.ColorKeysSerializer.WriteValue(value.colorKeys, writer);
+
+ if (ModeProperty != null)
+ {
+ try
+ {
+ EnumSerializer.WriteValue("mode", ModeProperty.GetValue(value, null), writer);
+ }
+ catch (Exception)
+ {
+ writer.Context.Config.DebugContext.LogWarning("Failed to write Gradient.mode, due to Unity's API disallowing setting of this member on other threads than the main thread. Gradient.mode will have been lost upon deserialization.");
+ }
+
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta
new file mode 100644
index 00000000..8c4ad104
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d5b54660d5342fd45e2e43775538879d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs
new file mode 100644
index 00000000..c2aa73e1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs
@@ -0,0 +1,121 @@
+//-----------------------------------------------------------------------
+// <copyright file="KeyframeFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(KeyframeFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using VRC.Udon.Serialization.OdinSerializer.Utilities;
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Keyframe"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Keyframe}" />
+ public class KeyframeFormatter : MinimalBaseFormatter<Keyframe>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+ private static readonly Serializer<int> IntSerializer = Serializer.Get<int>();
+
+ private static readonly bool Is_In_2018_1_Or_Above;
+ private static IFormatter<Keyframe> Formatter;
+
+ static KeyframeFormatter()
+ {
+ Is_In_2018_1_Or_Above = typeof(Keyframe).GetProperty("weightedMode") != null;
+
+ if (Is_In_2018_1_Or_Above)
+ {
+ if (EmitUtilities.CanEmit)
+ {
+ Formatter = (IFormatter<Keyframe>)FormatterEmitter.GetEmittedFormatter(typeof(Keyframe), SerializationPolicies.Everything);
+ }
+ else
+ {
+ Formatter = new ReflectionFormatter<Keyframe>(SerializationPolicies.Everything);
+ }
+ }
+ }
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Keyframe value, IDataReader reader)
+ {
+ string name;
+ EntryType first = reader.PeekEntry(out name);
+
+ if (first == EntryType.Integer && name == "ver")
+ {
+ if (Formatter == null)
+ {
+ // We're deserializing 2018.1+ data in a lower version of Unity - so just give it a go
+ Formatter = new ReflectionFormatter<Keyframe>(SerializationPolicies.Everything);
+ }
+
+ int version;
+ reader.ReadInt32(out version);
+
+ // Only one version so far, so ignore it for now
+ value = Formatter.Deserialize(reader);
+ }
+ else
+ {
+ // Legacy Keyframe deserialization code
+ value.inTangent = KeyframeFormatter.FloatSerializer.ReadValue(reader);
+ value.outTangent = KeyframeFormatter.FloatSerializer.ReadValue(reader);
+ value.time = KeyframeFormatter.FloatSerializer.ReadValue(reader);
+ value.value = KeyframeFormatter.FloatSerializer.ReadValue(reader);
+
+#pragma warning disable 0618
+ value.tangentMode = KeyframeFormatter.IntSerializer.ReadValue(reader);
+#pragma warning restore 0618
+ }
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Keyframe value, IDataWriter writer)
+ {
+ if (Is_In_2018_1_Or_Above)
+ {
+ writer.WriteInt32("ver", 1);
+ Formatter.Serialize(value, writer);
+ }
+ else
+ {
+ // Legacy Keyframe serialization code
+ KeyframeFormatter.FloatSerializer.WriteValue(value.inTangent, writer);
+ KeyframeFormatter.FloatSerializer.WriteValue(value.outTangent, writer);
+ KeyframeFormatter.FloatSerializer.WriteValue(value.time, writer);
+ KeyframeFormatter.FloatSerializer.WriteValue(value.value, writer);
+
+#pragma warning disable 0618
+ KeyframeFormatter.IntSerializer.WriteValue(value.tangentMode, writer);
+#pragma warning restore 0618
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta
new file mode 100644
index 00000000..785081be
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 68ac0b27f571616d3ed26c23eef40c8c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs
new file mode 100644
index 00000000..4231e03c
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs
@@ -0,0 +1,55 @@
+//-----------------------------------------------------------------------
+// <copyright file="LayerMaskFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(LayerMaskFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="LayerMask"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.LayerMask}" />
+ public class LayerMaskFormatter : MinimalBaseFormatter<LayerMask>
+ {
+ private static readonly Serializer<int> IntSerializer = Serializer.Get<int>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref LayerMask value, IDataReader reader)
+ {
+ value.value = LayerMaskFormatter.IntSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref LayerMask value, IDataWriter writer)
+ {
+ LayerMaskFormatter.IntSerializer.WriteValue(value.value, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta
new file mode 100644
index 00000000..d24fe609
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: afc596cd95a1ac316024d16f6fec6536
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs
new file mode 100644
index 00000000..230fcc8f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="QuaternionFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(QuaternionFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Quaternion"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Quaternion}" />
+ public class QuaternionFormatter : MinimalBaseFormatter<Quaternion>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Quaternion value, IDataReader reader)
+ {
+ value.x = QuaternionFormatter.FloatSerializer.ReadValue(reader);
+ value.y = QuaternionFormatter.FloatSerializer.ReadValue(reader);
+ value.z = QuaternionFormatter.FloatSerializer.ReadValue(reader);
+ value.w = QuaternionFormatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Quaternion value, IDataWriter writer)
+ {
+ QuaternionFormatter.FloatSerializer.WriteValue(value.x, writer);
+ QuaternionFormatter.FloatSerializer.WriteValue(value.y, writer);
+ QuaternionFormatter.FloatSerializer.WriteValue(value.z, writer);
+ QuaternionFormatter.FloatSerializer.WriteValue(value.w, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta
new file mode 100644
index 00000000..ec7ce1cb
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 558323987bf9b75943382a5faa093ee3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs
new file mode 100644
index 00000000..bd309e5b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="RectFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(RectFormatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Rect"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Rect}" />
+ public class RectFormatter : MinimalBaseFormatter<Rect>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Rect value, IDataReader reader)
+ {
+ value.x = RectFormatter.FloatSerializer.ReadValue(reader);
+ value.y = RectFormatter.FloatSerializer.ReadValue(reader);
+ value.width = RectFormatter.FloatSerializer.ReadValue(reader);
+ value.height = RectFormatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Rect value, IDataWriter writer)
+ {
+ RectFormatter.FloatSerializer.WriteValue(value.x, writer);
+ RectFormatter.FloatSerializer.WriteValue(value.y, writer);
+ RectFormatter.FloatSerializer.WriteValue(value.width, writer);
+ RectFormatter.FloatSerializer.WriteValue(value.height, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta
new file mode 100644
index 00000000..3461fb01
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 196809b991e565a48e3d4ad08cb30b5e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs
new file mode 100644
index 00000000..a2568444
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs
@@ -0,0 +1,47 @@
+//VRC TCL: Disabled for security
+
+//-----------------------------------------------------------------------
+// <copyright file="UnityEventFormatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+// using VRC.Udon.Serialization.OdinSerializer;
+//
+// [assembly: RegisterFormatter(typeof(UnityEventFormatter<>))]
+//
+// namespace VRC.Udon.Serialization.OdinSerializer
+// {
+// using UnityEngine.Events;
+//
+// /// <summary>
+// /// Custom generic formatter for the <see cref="UnityEvent{T0}"/>, <see cref="UnityEvent{T0, T1}"/>, <see cref="UnityEvent{T0, T1, T2}"/> and <see cref="UnityEvent{T0, T1, T2, T3}"/> types.
+// /// </summary>
+// /// <typeparam name="T">The type of UnityEvent that this formatter can serialize and deserialize.</typeparam>
+// /// <seealso cref="ReflectionFormatter{UnityEngine.Events.UnityEvent}" />
+// public class UnityEventFormatter<T> : ReflectionFormatter<T> where T : UnityEventBase, new()
+// {
+// /// <summary>
+// /// Get an uninitialized object of type <see cref="T" />.
+// /// </summary>
+// /// <returns>
+// /// An uninitialized object of type <see cref="T" />.
+// /// </returns>
+// protected override T GetUninitializedObject()
+// {
+// return new T();
+// }
+// }
+// }
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta
new file mode 100644
index 00000000..cfc50f2b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c934302874ac3315ed322feefefa1f9c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs
new file mode 100644
index 00000000..eb6a8488
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs
@@ -0,0 +1,57 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector2Formatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(Vector2Formatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Vector2"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Vector2}" />
+ public class Vector2Formatter : MinimalBaseFormatter<Vector2>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Vector2 value, IDataReader reader)
+ {
+ value.x = Vector2Formatter.FloatSerializer.ReadValue(reader);
+ value.y = Vector2Formatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Vector2 value, IDataWriter writer)
+ {
+ Vector2Formatter.FloatSerializer.WriteValue(value.x, writer);
+ Vector2Formatter.FloatSerializer.WriteValue(value.y, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta
new file mode 100644
index 00000000..bc1ed4e4
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 70c675a7b4c71c685ee39d745ccb058b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs
new file mode 100644
index 00000000..139ad212
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs
@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector3Formatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(Vector3Formatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Vector3"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Vector3}" />
+ public class Vector3Formatter : MinimalBaseFormatter<Vector3>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Vector3 value, IDataReader reader)
+ {
+ value.x = Vector3Formatter.FloatSerializer.ReadValue(reader);
+ value.y = Vector3Formatter.FloatSerializer.ReadValue(reader);
+ value.z = Vector3Formatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Vector3 value, IDataWriter writer)
+ {
+ Vector3Formatter.FloatSerializer.WriteValue(value.x, writer);
+ Vector3Formatter.FloatSerializer.WriteValue(value.y, writer);
+ Vector3Formatter.FloatSerializer.WriteValue(value.z, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta
new file mode 100644
index 00000000..d0280f99
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: da2644647af1368176103aa87de1dbaf
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs
new file mode 100644
index 00000000..1beec915
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs
@@ -0,0 +1,61 @@
+//-----------------------------------------------------------------------
+// <copyright file="Vector4Formatter.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+using VRC.Udon.Serialization.OdinSerializer;
+
+[assembly: RegisterFormatter(typeof(Vector4Formatter))]
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Custom formatter for the <see cref="Vector4"/> type.
+ /// </summary>
+ /// <seealso cref="MinimalBaseFormatter{UnityEngine.Vector4}" />
+ public class Vector4Formatter : MinimalBaseFormatter<Vector4>
+ {
+ private static readonly Serializer<float> FloatSerializer = Serializer.Get<float>();
+
+ /// <summary>
+ /// Reads into the specified value using the specified reader.
+ /// </summary>
+ /// <param name="value">The value to read into.</param>
+ /// <param name="reader">The reader to use.</param>
+ protected override void Read(ref Vector4 value, IDataReader reader)
+ {
+ value.x = Vector4Formatter.FloatSerializer.ReadValue(reader);
+ value.y = Vector4Formatter.FloatSerializer.ReadValue(reader);
+ value.z = Vector4Formatter.FloatSerializer.ReadValue(reader);
+ value.w = Vector4Formatter.FloatSerializer.ReadValue(reader);
+ }
+
+ /// <summary>
+ /// Writes from the specified value using the specified writer.
+ /// </summary>
+ /// <param name="value">The value to write from.</param>
+ /// <param name="writer">The writer to use.</param>
+ protected override void Write(ref Vector4 value, IDataWriter writer)
+ {
+ Vector4Formatter.FloatSerializer.WriteValue(value.x, writer);
+ Vector4Formatter.FloatSerializer.WriteValue(value.y, writer);
+ Vector4Formatter.FloatSerializer.WriteValue(value.z, writer);
+ Vector4Formatter.FloatSerializer.WriteValue(value.w, writer);
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta
new file mode 100644
index 00000000..7e912d48
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 60afa8ede3981c383782a01ddc55e943
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs
new file mode 100644
index 00000000..4a870b1c
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs
@@ -0,0 +1,156 @@
+//-----------------------------------------------------------------------
+// <copyright file="OdinPrefabSerializationEditorUtility.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+//#define PREFAB_DEBUG
+#if UNITY_EDITOR
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+ using System.Reflection;
+ using UnityEditor;
+ using UnityEngine;
+
+ public static class OdinPrefabSerializationEditorUtility
+ {
+ private static bool? hasNewPrefabWorkflow;
+ private static MethodInfo PrefabUtility_GetPrefabAssetType_Method = typeof(PrefabUtility).GetMethod("GetPrefabAssetType", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(UnityEngine.Object) }, null);
+ private static MethodInfo PrefabUtility_GetPrefabParent_Method = typeof(PrefabUtility).GetMethod("GetPrefabParent", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(UnityEngine.Object) }, null);
+ private static MethodInfo PrefabUtility_GetCorrespondingObjectFromSource_Method = typeof(PrefabUtility).GetMethod("GetCorrespondingObjectFromSource", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(UnityEngine.Object) }, null);
+ private static MethodInfo PrefabUtility_GetPrefabType_Method = typeof(PrefabUtility).GetMethod("GetPrefabType", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(UnityEngine.Object) }, null);
+ private static MethodInfo PrefabUtility_ApplyPropertyOverride_Method;
+
+ static OdinPrefabSerializationEditorUtility()
+ {
+ Type interactionModeEnum = typeof(UnityEditor.Editor).Assembly.GetType("UnityEditor.InteractionMode");
+
+ if (interactionModeEnum != null)
+ {
+ PrefabUtility_ApplyPropertyOverride_Method = typeof(PrefabUtility).GetMethod("ApplyPropertyOverride", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(SerializedProperty), typeof(string), interactionModeEnum }, null);
+ }
+ }
+
+ public static bool HasNewPrefabWorkflow
+ {
+ get
+ {
+ if (hasNewPrefabWorkflow == null)
+ {
+ hasNewPrefabWorkflow = DetectNewPrefabWorkflow();
+ }
+
+ return hasNewPrefabWorkflow.Value;
+ }
+ }
+
+ public static bool HasApplyPropertyOverride
+ {
+ get
+ {
+ return PrefabUtility_ApplyPropertyOverride_Method != null;
+ }
+ }
+
+ private static bool DetectNewPrefabWorkflow()
+ {
+ try
+ {
+ var method = typeof(PrefabUtility).GetMethod("GetPrefabType", BindingFlags.Public | BindingFlags.Static, null, new Type[] { typeof(UnityEngine.Object) }, null);
+
+ if (method == null) return true;
+
+ if (method.IsDefined(typeof(ObsoleteAttribute), false))
+ {
+ return true;
+ }
+
+ return false;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static void ApplyPropertyOverride(SerializedProperty instanceProperty, string assetPath)
+ {
+ //PrefabUtility.ApplyPropertyOverride(instanceProperty, assetPath, InteractionMode.AutomatedAction);
+ if (!HasApplyPropertyOverride) throw new NotSupportedException("PrefabUtility.ApplyPropertyOverride doesn't exist in this version of Unity");
+ PrefabUtility_ApplyPropertyOverride_Method.Invoke(null, new object[] { instanceProperty, assetPath, 0 });
+ }
+
+ public static bool ObjectIsPrefabInstance(UnityEngine.Object unityObject)
+ {
+ if (PrefabUtility_GetPrefabType_Method != null)
+ {
+ try
+ {
+ int prefabType = Convert.ToInt32((Enum)PrefabUtility_GetPrefabType_Method.Invoke(null, new object[] { unityObject }));
+ // PrefabType.PrefabInstance == 3
+ if (prefabType == 3) return true;
+ }
+ catch (Exception) { }
+ }
+
+ if (PrefabUtility_GetPrefabAssetType_Method != null)
+ {
+ int prefabAssetType = Convert.ToInt32((Enum)PrefabUtility_GetPrefabAssetType_Method.Invoke(null, new object[] { unityObject }));
+ // 1 = PrefabAssetType.Regular
+ // 3 = PrefabAssetType.Variant
+ return prefabAssetType == 1 || prefabAssetType == 3;
+ }
+
+ if (PrefabUtility_GetPrefabType_Method == null && PrefabUtility_GetPrefabAssetType_Method == null)
+ {
+ Debug.LogError("Neither PrefabUtility.GetPrefabType or PrefabUtility.GetPrefabAssetType methods could be located. Prefab functionality will likely be broken in this build of Odin.");
+ }
+
+ return GetCorrespondingObjectFromSource(unityObject) != null;
+ }
+
+ public static bool ObjectHasNestedOdinPrefabData(UnityEngine.Object unityObject)
+ {
+ if (!HasNewPrefabWorkflow) return false;
+ if (!(unityObject is ISupportsPrefabSerialization)) return false;
+ var prefab = GetCorrespondingObjectFromSource(unityObject);
+ return IsOdinSerializedPrefabInstance(prefab);
+ }
+
+ private static bool IsOdinSerializedPrefabInstance(UnityEngine.Object unityObject)
+ {
+ if (!(unityObject is ISupportsPrefabSerialization)) return false;
+ return GetCorrespondingObjectFromSource(unityObject) != null;
+ }
+
+ public static UnityEngine.Object GetCorrespondingObjectFromSource(UnityEngine.Object unityObject)
+ {
+ if (PrefabUtility_GetCorrespondingObjectFromSource_Method != null)
+ {
+ return (UnityEngine.Object)PrefabUtility_GetCorrespondingObjectFromSource_Method.Invoke(null, new object[] { unityObject });
+ }
+
+ if (PrefabUtility_GetPrefabParent_Method != null)
+ {
+ return (UnityEngine.Object)PrefabUtility_GetPrefabParent_Method.Invoke(null, new object[] { unityObject });
+ }
+
+ Debug.LogError("Neither PrefabUtility.GetCorrespondingObjectFromSource or PrefabUtility.GetPrefabParent methods could be located. Prefab functionality will be broken in this build of Odin.");
+ return null;
+ }
+ }
+}
+#endif \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta
new file mode 100644
index 00000000..4e44bfb1
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aaf2f90207414827b53b85dae0eae82e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects.meta
new file mode 100644
index 00000000..81f64b31
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2fafac9a46c188b409d374352cf956c3
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs
new file mode 100644
index 00000000..4771de1b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOverridesSerializationFormat.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ /// <summary>
+ /// Indicates that an Odin-serialized Unity object controls its own serialization format. Every time it is serialized, it will be asked which format to use.
+ /// </summary>
+ public interface IOverridesSerializationFormat
+ {
+ /// <summary>
+ /// Gets the format to use for serialization.
+ /// </summary>
+ DataFormat GetFormatToSerializeAs(bool isPlayer);
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta
new file mode 100644
index 00000000..9ec81ef0
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ff1ca109149d83b03b39644f8045275e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs
new file mode 100644
index 00000000..6f46532f
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs
@@ -0,0 +1,31 @@
+//-----------------------------------------------------------------------
+// <copyright file="IOverridesSerializationPolicy.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ /// <summary>
+ /// Indicates that an Odin-serialized Unity object provides its own serialization policy rather than using the default policy.
+ /// <para/>
+ /// Note that THE VALUES RETURNED BY THIS INTERFACE WILL OVERRIDE THE PARAMETERS PASSED TO <see cref="UnitySerializationUtility.SerializeUnityObject(UnityEngine.Object, ref SerializationData, bool, SerializationContext)"/> and <see cref="UnitySerializationUtility.DeserializeUnityObject(UnityEngine.Object, ref SerializationData, DeserializationContext)"/>.
+ /// </summary>
+ public interface IOverridesSerializationPolicy
+ {
+ ISerializationPolicy SerializationPolicy { get; }
+ bool OdinSerializesUnityFields { get; }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta
new file mode 100644
index 00000000..57a0c8a0
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8942002e9ac41c2bfd27c4fbedf93f09
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs
new file mode 100644
index 00000000..507eaa70
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs
@@ -0,0 +1,30 @@
+//-----------------------------------------------------------------------
+// <copyright file="ISupportsPrefabSerialization.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ /// <summary>
+ /// Indicates that an Odin-serialized Unity object supports prefab serialization.
+ /// </summary>
+ public interface ISupportsPrefabSerialization
+ {
+ /// <summary>
+ /// Gets or sets the serialization data of the object.
+ /// </summary>
+ SerializationData SerializationData { get; set; }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta
new file mode 100644
index 00000000..1023fe57
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7279ec8ad7837f13ec833193ab4282cc
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs
new file mode 100644
index 00000000..8b441751
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs
@@ -0,0 +1,175 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializationData.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System;
+ using System.Collections.Generic;
+ using UnityEngine;
+ using Utilities;
+
+ /// <summary>
+ /// Unity serialized data struct that contains all data needed by Odin serialization.
+ /// </summary>
+ [Serializable]
+ public struct SerializationData
+ {
+ /// <summary>
+ /// The name of the <see cref="PrefabModificationsReferencedUnityObjects"/> field.
+ /// </summary>
+ public const string PrefabModificationsReferencedUnityObjectsFieldName = "PrefabModificationsReferencedUnityObjects";
+
+ /// <summary>
+ /// The name of the <see cref="PrefabModifications"/> field.
+ /// </summary>
+ public const string PrefabModificationsFieldName = "PrefabModifications";
+
+ /// <summary>
+ /// The name of the <see cref="Prefab"/> field.
+ /// </summary>
+ public const string PrefabFieldName = "Prefab";
+
+ /// <summary>
+ /// The data format used by the serializer. This field will be automatically set to the format specified in the global serialization config
+ /// when the Unity object gets serialized, unless the Unity object implements the <see cref="IOverridesSerializationFormat"/> interface.
+ /// </summary>
+ [SerializeField]
+ public DataFormat SerializedFormat;
+
+ /// <summary>
+ /// The serialized data when serializing with the Binray format.
+ /// </summary>
+ [SerializeField]
+ public byte[] SerializedBytes;
+
+ /// <summary>
+ /// All serialized Unity references.
+ /// </summary>
+ [SerializeField]
+ public List<UnityEngine.Object> ReferencedUnityObjects;
+
+ /// <summary>
+ /// Whether the object contains any serialized data.
+ /// </summary>
+ [Obsolete("Use ContainsData instead")]
+ public bool HasEditorData
+ {
+ get
+ {
+ switch (this.SerializedFormat)
+ {
+ case DataFormat.Binary:
+ case DataFormat.JSON:
+ return !(this.SerializedBytesString.IsNullOrWhitespace() && (this.SerializedBytes == null || this.SerializedBytes.Length == 0));
+
+ case DataFormat.Nodes:
+ return !(this.SerializationNodes == null || this.SerializationNodes.Count == 0);
+
+ default:
+ throw new NotImplementedException(this.SerializedFormat.ToString());
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether the struct contains any data.
+ /// If this is false, then it could mean that Unity has not yet deserialized the struct.
+ /// </summary>
+ public bool ContainsData
+ {
+ get
+ {
+ return
+ // this.SerializedBytesString != null && // Unity serialized strings remains null when an object is created until it's deserialized.
+ this.SerializedBytes != null &&
+ this.SerializationNodes != null &&
+ this.PrefabModifications != null &&
+ this.ReferencedUnityObjects != null;
+ }
+ }
+
+ /// <summary>
+ /// The serialized data when serializing with the JSON format.
+ /// </summary>
+ [SerializeField]
+ public string SerializedBytesString;
+
+ /// <summary>
+ /// The reference to the prefab this is only populated in prefab scene instances.
+ /// </summary>
+ [SerializeField]
+ public UnityEngine.Object Prefab;
+
+ /// <summary>
+ /// All serialized Unity references.
+ /// </summary>
+ [SerializeField]
+ public List<UnityEngine.Object> PrefabModificationsReferencedUnityObjects;
+
+ /// <summary>
+ /// All Odin serialized prefab modifications.
+ /// </summary>
+ [SerializeField]
+ public List<string> PrefabModifications;
+
+ /// <summary>
+ /// The serialized data when serializing with the Nodes format.
+ /// </summary>
+ [SerializeField]
+ public List<SerializationNode> SerializationNodes;
+
+ /// <summary>
+ /// Resets all data.
+ /// </summary>
+ public void Reset()
+ {
+ this.SerializedFormat = DataFormat.Binary;
+
+ if (this.SerializedBytes != null && this.SerializedBytes.Length > 0)
+ {
+ this.SerializedBytes = new byte[0];
+ }
+
+ if (this.ReferencedUnityObjects != null && this.ReferencedUnityObjects.Count > 0)
+ {
+ this.ReferencedUnityObjects.Clear();
+ }
+
+ this.Prefab = null;
+
+ if (this.SerializationNodes != null && this.SerializationNodes.Count > 0)
+ {
+ this.SerializationNodes.Clear();
+ }
+
+ if (this.SerializedBytesString != null && this.SerializedBytesString.Length > 0)
+ {
+ this.SerializedBytesString = string.Empty;
+ }
+
+ if (this.PrefabModificationsReferencedUnityObjects != null && this.PrefabModificationsReferencedUnityObjects.Count > 0)
+ {
+ this.PrefabModificationsReferencedUnityObjects.Clear();
+ }
+
+ if (this.PrefabModifications != null && this.PrefabModifications.Count > 0)
+ {
+ this.PrefabModifications.Clear();
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta
new file mode 100644
index 00000000..95b776f7
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ea095023abd05a7af0da4166dcefdee8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs
new file mode 100644
index 00000000..f31b8b4d
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedBehaviour.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity Behaviour which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedBehaviour : Behaviour, ISerializationCallbackReceiver, ISupportsPrefabSerialization
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta
new file mode 100644
index 00000000..605a78ee
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c3cecb461cebbc940ede3b5ddb72382e
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs
new file mode 100644
index 00000000..47ba4f3b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedComponent.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity Component which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedComponent : Component, ISerializationCallbackReceiver, ISupportsPrefabSerialization
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta
new file mode 100644
index 00000000..903c320b
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 56b88cfe9935184fe250bda018144f26
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs
new file mode 100644
index 00000000..8d5ea413
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs
@@ -0,0 +1,63 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedMonoBehaviour.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity MonoBehaviour which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedMonoBehaviour : MonoBehaviour, ISerializationCallbackReceiver, ISupportsPrefabSerialization
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ SerializationData ISupportsPrefabSerialization.SerializationData { get { return this.serializationData; } set { this.serializationData = value; } }
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta
new file mode 100644
index 00000000..8a926e47
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d1b9fa6342beb9fdfc2c4bc1d8e5e971
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs
new file mode 100644
index 00000000..f0af64ee
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedScriptableObject.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity ScriptableObject which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedScriptableObject : ScriptableObject, ISerializationCallbackReceiver
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta
new file mode 100644
index 00000000..72be5810
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6cb9325ffffee5d6ed94d71590b4049a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs
new file mode 100644
index 00000000..85a01e97
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedStateMachineBehaviour.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity StateMachineBehaviour which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedStateMachineBehaviour : StateMachineBehaviour, ISerializationCallbackReceiver
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta
new file mode 100644
index 00000000..442e5312
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: eefcd68a84ee7903b08c6254c17fafe3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs
new file mode 100644
index 00000000..86a75a75
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs
@@ -0,0 +1,60 @@
+//-----------------------------------------------------------------------
+// <copyright file="SerializedUnityObject.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// A Unity ScriptableObject which is serialized by the Sirenix serialization system.
+ /// </summary>
+#if ODIN_INSPECTOR
+ [Sirenix.OdinInspector.ShowOdinSerializedPropertiesInInspector]
+#endif
+
+ public abstract class SerializedUnityObject : UnityEngine.Object, ISerializationCallbackReceiver
+ {
+ [SerializeField, HideInInspector]
+ private SerializationData serializationData;
+
+ void ISerializationCallbackReceiver.OnAfterDeserialize()
+ {
+ UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData);
+ this.OnAfterDeserialize();
+ }
+
+ void ISerializationCallbackReceiver.OnBeforeSerialize()
+ {
+ this.OnBeforeSerialize();
+ UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData);
+ }
+
+ /// <summary>
+ /// Invoked after deserialization has taken place.
+ /// </summary>
+ protected virtual void OnAfterDeserialize()
+ {
+ }
+
+ /// <summary>
+ /// Invoked before serialization has taken place.
+ /// </summary>
+ protected virtual void OnBeforeSerialize()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta
new file mode 100644
index 00000000..738d24b4
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d62f7ab4e5aa075b819d6c71e929686b
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs
new file mode 100644
index 00000000..a7cff020
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs
@@ -0,0 +1,160 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnityReferenceResolver.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Collections.Generic;
+ using Utilities;
+
+ /// <summary>
+ /// Resolves external index references to Unity objects.
+ /// </summary>
+ /// <seealso cref="IExternalIndexReferenceResolver" />
+ /// <seealso cref="ICacheNotificationReceiver" />
+ public sealed class UnityReferenceResolver : IExternalIndexReferenceResolver, ICacheNotificationReceiver
+ {
+ private Dictionary<UnityEngine.Object, int> referenceIndexMapping = new Dictionary<UnityEngine.Object, int>(32, ReferenceEqualityComparer<UnityEngine.Object>.Default);
+ private List<UnityEngine.Object> referencedUnityObjects;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnityReferenceResolver"/> class.
+ /// </summary>
+ public UnityReferenceResolver()
+ {
+ this.referencedUnityObjects = new List<UnityEngine.Object>();
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="UnityReferenceResolver"/> class with a list of Unity objects.
+ /// </summary>
+ /// <param name="referencedUnityObjects">The referenced Unity objects.</param>
+ public UnityReferenceResolver(List<UnityEngine.Object> referencedUnityObjects)
+ {
+ this.SetReferencedUnityObjects(referencedUnityObjects);
+ }
+
+ /// <summary>
+ /// Gets the currently referenced Unity objects.
+ /// </summary>
+ /// <returns>A list of the currently referenced Unity objects.</returns>
+ public List<UnityEngine.Object> GetReferencedUnityObjects()
+ {
+ return this.referencedUnityObjects;
+ }
+
+ /// <summary>
+ /// Sets the referenced Unity objects of the resolver to a given list, or a new list if the value is null.
+ /// </summary>
+ /// <param name="referencedUnityObjects">The referenced Unity objects to set, or null if a new list is required.</param>
+ public void SetReferencedUnityObjects(List<UnityEngine.Object> referencedUnityObjects)
+ {
+ if (referencedUnityObjects == null)
+ {
+ referencedUnityObjects = new List<UnityEngine.Object>();
+ }
+
+ this.referencedUnityObjects = referencedUnityObjects;
+ this.referenceIndexMapping.Clear();
+
+ for (int i = 0; i < this.referencedUnityObjects.Count; i++)
+ {
+ if (object.ReferenceEquals(this.referencedUnityObjects[i], null) == false)
+ {
+ if (!this.referenceIndexMapping.ContainsKey(this.referencedUnityObjects[i]))
+ {
+ this.referenceIndexMapping.Add(this.referencedUnityObjects[i], i);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Determines whether the specified value can be referenced externally via this resolver.
+ /// </summary>
+ /// <param name="value">The value to reference.</param>
+ /// <param name="index">The index of the resolved value, if it can be referenced.</param>
+ /// <returns>
+ /// <c>true</c> if the reference can be resolved, otherwise <c>false</c>.
+ /// </returns>
+ public bool CanReference(object value, out int index)
+ {
+ if (this.referencedUnityObjects == null)
+ {
+ this.referencedUnityObjects = new List<UnityEngine.Object>(32);
+ }
+
+ var obj = value as UnityEngine.Object;
+
+ if (object.ReferenceEquals(null, obj) == false)
+ {
+ if (this.referenceIndexMapping.TryGetValue(obj, out index) == false)
+ {
+ index = this.referencedUnityObjects.Count;
+ this.referenceIndexMapping.Add(obj, index);
+ this.referencedUnityObjects.Add(obj);
+ }
+
+ return true;
+ }
+
+ index = -1;
+ return false;
+ }
+
+ /// <summary>
+ /// Tries to resolve the given reference index to a reference value.
+ /// </summary>
+ /// <param name="index">The index to resolve.</param>
+ /// <param name="value">The resolved value.</param>
+ /// <returns>
+ /// <c>true</c> if the index could be resolved to a value, otherwise <c>false</c>.
+ /// </returns>
+ public bool TryResolveReference(int index, out object value)
+ {
+ if (this.referencedUnityObjects == null || index < 0 || index >= this.referencedUnityObjects.Count)
+ {
+ // Sometimes something has destroyed the list of references in between serialization and deserialization
+ // (Unity prefab instances are especially bad at preserving such data), and in these cases we still don't
+ // want the system to fall back to a formatter, so we give out a null value.
+ value = null;
+ return true;
+ }
+
+ value = this.referencedUnityObjects[index];
+ return true;
+ }
+
+ /// <summary>
+ /// Resets this instance.
+ /// </summary>
+ public void Reset()
+ {
+ this.referencedUnityObjects = null;
+ this.referenceIndexMapping.Clear();
+ }
+
+ void ICacheNotificationReceiver.OnFreed()
+ {
+ this.Reset();
+ }
+
+ void ICacheNotificationReceiver.OnClaimed()
+ {
+ }
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta
new file mode 100644
index 00000000..2a0284bb
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: de5584f66ccc5e072681a310c5987b8c
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs
new file mode 100644
index 00000000..74cf6983
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs
@@ -0,0 +1,100 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnitySerializationInitializer.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using UnityEngine;
+
+ /// <summary>
+ /// Utility class which initializes the Sirenix serialization system to be compatible with Unity.
+ /// </summary>
+ public static class UnitySerializationInitializer
+ {
+ private static readonly object LOCK = new object();
+ private static bool initialized = false;
+
+ public static bool Initialized { get { return initialized; } }
+
+ public static RuntimePlatform CurrentPlatform { get; private set; }
+
+ /// <summary>
+ /// Initializes the Sirenix serialization system to be compatible with Unity.
+ /// </summary>
+ public static void Initialize()
+ {
+ if (!initialized)
+ {
+ lock (LOCK)
+ {
+ if (!initialized)
+ {
+ try
+ {
+ // Ensure that the config instance is loaded before deserialization of anything occurs.
+ // If we try to load it during deserialization, Unity will throw exceptions, as a lot of
+ // the Unity API is disallowed during serialization and deserialization.
+ GlobalSerializationConfig.LoadInstanceIfAssetExists();
+
+ CurrentPlatform = Application.platform;
+
+ if (Application.isEditor) return;
+
+ ArchitectureInfo.SetRuntimePlatform(CurrentPlatform);
+
+ //if (CurrentPlatform == RuntimePlatform.Android)
+ //{
+ // //using (var system = new AndroidJavaClass("java.lang.System"))
+ // //{
+ // // string architecture = system.CallStatic<string>("getProperty", "os.arch");
+ // // ArchitectureInfo.SetIsOnAndroid(architecture);
+ // //}
+ //}
+ //else if (CurrentPlatform == RuntimePlatform.IPhonePlayer)
+ //{
+ // ArchitectureInfo.SetIsOnIPhone();
+ //}
+ //else
+ //{
+ // ArchitectureInfo.SetIsNotOnMobile();
+ //}
+ }
+ finally
+ {
+ initialized = true;
+ }
+ }
+ }
+ }
+ }
+
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
+ private static void InitializeRuntime()
+ {
+ Initialize();
+ }
+
+#if UNITY_EDITOR
+
+ [UnityEditor.InitializeOnLoadMethod]
+ private static void InitializeEditor()
+ {
+ Initialize();
+ }
+#endif
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta
new file mode 100644
index 00000000..59e74aa5
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f670c1f9aa9ab0c5988e81802c005767
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs
new file mode 100644
index 00000000..d7b77731
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs
@@ -0,0 +1,2788 @@
+//-----------------------------------------------------------------------
+// <copyright file="UnitySerializationUtility.cs" company="Sirenix IVS">
+// Copyright (c) 2018 Sirenix IVS
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// </copyright>
+//-----------------------------------------------------------------------
+
+//#define PREFAB_DEBUG
+
+namespace VRC.Udon.Serialization.OdinSerializer
+{
+ using System.Globalization;
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using Utilities;
+ using UnityEngine;
+ using UnityEngine.Events;
+ using System.Runtime.CompilerServices;
+ using UnityEngine.Assertions;
+ using System.Runtime.Serialization;
+
+#pragma warning disable 0618 // Obsolete Methods // VRC
+
+#if PREFAB_DEBUG && !SIRENIX_INTERNAL
+#warning "Prefab serialization debugging is enabled outside of Sirenix internal. Are you sure this is right?"
+#endif
+
+ /// <summary>
+ /// Provides an array of utility wrapper methods for easy serialization and deserialization of Unity objects of any type.
+ /// Note that, during serialization, it is always assumed that we are running on Unity's main thread. Deserialization can
+ /// happen on any thread, and all API's interacting with deserialization are thread-safe.
+ /// <para />
+ /// Note that setting the IndexReferenceResolver on contexts passed into methods on this class will have no effect, as it will always
+ /// be set to a UnityReferenceResolver.
+ /// </summary>
+ public static class UnitySerializationUtility
+ {
+ public static readonly Type SerializeReferenceAttributeType = typeof(SerializeField).Assembly.GetType("UnityEngine.SerializeReference");
+
+ private static readonly Assembly String_Assembly = typeof(string).Assembly;
+ private static readonly Assembly HashSet_Assembly = typeof(HashSet<>).Assembly;
+ private static readonly Assembly LinkedList_Assembly = typeof(LinkedList<>).Assembly;
+
+#if UNITY_EDITOR
+ /// <summary>
+ /// From the new scriptable build pipeline package
+ /// </summary>
+ [NonSerialized]
+ private static readonly Type SBP_ContentPipelineType = TwoWaySerializationBinder.Default.BindToType("UnityEditor.Build.Pipeline.ContentPipeline");
+
+ [NonSerialized]
+ private static readonly MethodInfo PrefabUtility_IsComponentAddedToPrefabInstance_MethodInfo = typeof(UnityEditor.PrefabUtility).GetMethod("IsComponentAddedToPrefabInstance");
+
+ [NonSerialized]
+ private static readonly HashSet<UnityEngine.Object> UnityObjectsWaitingForDelayedModificationApply = new HashSet<UnityEngine.Object>(ReferenceEqualityComparer<UnityEngine.Object>.Default);
+
+ [NonSerialized]
+ private static readonly Dictionary<UnityEngine.Object, List<PrefabModification>> RegisteredPrefabModifications = new Dictionary<UnityEngine.Object, List<PrefabModification>>(ReferenceEqualityComparer<UnityEngine.Object>.Default);
+
+ private static class PrefabDeserializeUtility
+ {
+ private static int updateCount = 0;
+
+ [NonSerialized]
+ public static readonly HashSet<UnityEngine.Object> PrefabsWithValuesApplied = new HashSet<UnityEngine.Object>(ReferenceEqualityComparer<UnityEngine.Object>.Default);
+
+ [NonSerialized]
+ private static readonly Dictionary<UnityEngine.Object, HashSet<object>> SceneObjectsToKeepOnApply = new Dictionary<UnityEngine.Object, HashSet<object>>(ReferenceEqualityComparer<UnityEngine.Object>.Default);
+
+ [NonSerialized]
+ public static readonly object DeserializePrefabs_LOCK = new object();
+
+ private static readonly List<UnityEngine.Object> toRemove = new List<UnityEngine.Object>();
+
+ static PrefabDeserializeUtility()
+ {
+ UnityEditor.EditorApplication.update += OnEditorUpdate;
+ }
+
+ /// <summary>
+ /// Note: it is assumed that code calling this is holding the DeserializePrefabCaches_LOCK lock, and will continue to hold it while the returned hashset is being modified
+ /// </summary>
+ public static HashSet<object> GetSceneObjectsToKeepSet(UnityEngine.Object unityObject, bool createIfDoesntExist)
+ {
+ HashSet<object> keep;
+
+ if (!SceneObjectsToKeepOnApply.TryGetValue(unityObject, out keep))
+ {
+ keep = new HashSet<object>(ReferenceEqualityComparer<object>.Default);
+ SceneObjectsToKeepOnApply.Add(unityObject, keep);
+ }
+
+ return keep;
+ }
+
+ public static void CleanSceneObjectToKeepOnApply()
+ {
+ lock (DeserializePrefabs_LOCK)
+ {
+ foreach (var obj in SceneObjectsToKeepOnApply.Keys)
+ {
+ if (obj == null)
+ {
+ toRemove.Add(obj);
+ }
+ }
+
+ for (int i = 0; i < toRemove.Count; i++)
+ {
+ SceneObjectsToKeepOnApply.Remove(toRemove[i]);
+ }
+
+ toRemove.Clear();
+ }
+ }
+
+ private static void OnEditorUpdate()
+ {
+ lock (DeserializePrefabs_LOCK)
+ {
+ updateCount++;
+
+ if (updateCount >= 1000)
+ {
+ SceneObjectsToKeepOnApply.Clear();
+ updateCount = 0;
+ }
+ }
+ }
+ }
+
+#endif
+
+ // Note: Code that accesses any of these four caches should lock on the cache data structure itself
+ private static readonly Dictionary<MemberInfo, WeakValueGetter> UnityMemberGetters = new Dictionary<MemberInfo, WeakValueGetter>();
+ private static readonly Dictionary<MemberInfo, WeakValueSetter> UnityMemberSetters = new Dictionary<MemberInfo, WeakValueSetter>();
+
+ private static readonly Dictionary<MemberInfo, bool> UnityWillSerializeMembersCache = new Dictionary<MemberInfo, bool>();
+ private static readonly Dictionary<Type, bool> UnityWillSerializeTypesCache = new Dictionary<Type, bool>();
+
+ private static readonly HashSet<Type> UnityNeverSerializesTypes = new HashSet<Type>()
+ {
+ typeof(Coroutine)
+ };
+
+ private static readonly HashSet<string> UnityNeverSerializesTypeNames = new HashSet<string>()
+ {
+ "UnityEngine.AnimationState"
+ };
+
+#if UNITY_EDITOR
+
+ /// <summary>
+ /// Whether to always force editor mode serialization. This member only exists in the editor.
+ /// </summary>
+ public static bool ForceEditorModeSerialization { get; set; }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static List<PrefabModification> GetRegisteredPrefabModifications(UnityEngine.Object obj)
+ {
+ List<PrefabModification> result;
+ RegisteredPrefabModifications.TryGetValue(obj, out result);
+ return result;
+ }
+
+ public static bool HasModificationsWaitingForDelayedApply(UnityEngine.Object obj)
+ {
+ return UnityObjectsWaitingForDelayedModificationApply.Contains(obj);
+ }
+
+#endif
+
+ /// <summary>
+ /// Checks whether Odin will serialize a given member.
+ /// </summary>
+ /// <param name="member">The member to check.</param>
+ /// <param name="serializeUnityFields">Whether to allow serialization of members that will also be serialized by Unity.</param>
+ /// <param name="policy">The policy that Odin should be using for serialization of the given member. If this parameter is null, it defaults to <see cref="SerializationPolicies.Unity"/>.</param>
+ /// <returns>True if Odin will serialize the member, otherwise false.</returns>
+ public static bool OdinWillSerialize(MemberInfo member, bool serializeUnityFields, ISerializationPolicy policy = null)
+ {
+ Dictionary<MemberInfo, CachedSerializationBackendResult> cacheForPolicy;
+
+ if (policy == null || object.ReferenceEquals(policy, UnityPolicy))
+ {
+ cacheForPolicy = OdinWillSerializeCache_UnityPolicy;
+ }
+ else if (object.ReferenceEquals(policy, EverythingPolicy))
+ {
+ cacheForPolicy = OdinWillSerializeCache_EverythingPolicy;
+ }
+ else if (object.ReferenceEquals(policy, StrictPolicy))
+ {
+ cacheForPolicy = OdinWillSerializeCache_StrictPolicy;
+ }
+ else
+ {
+ lock (OdinWillSerializeCache_CustomPolicies)
+ {
+ if (!OdinWillSerializeCache_CustomPolicies.TryGetValue(policy, out cacheForPolicy))
+ {
+ cacheForPolicy = new Dictionary<MemberInfo, CachedSerializationBackendResult>(ReferenceEqualityComparer<MemberInfo>.Default);
+ OdinWillSerializeCache_CustomPolicies.Add(policy, cacheForPolicy);
+ }
+ }
+ }
+
+ CachedSerializationBackendResult result;
+
+ lock (cacheForPolicy)
+ {
+ if (!cacheForPolicy.TryGetValue(member, out result))
+ {
+ result = default(CachedSerializationBackendResult);
+
+ if (serializeUnityFields)
+ {
+ result.SerializeUnityFieldsTrueResult = CalculateOdinWillSerialize(member, serializeUnityFields, policy ?? UnityPolicy);
+ result.HasCalculatedSerializeUnityFieldsTrueResult = true;
+ }
+ else
+ {
+ result.SerializeUnityFieldsFalseResult = CalculateOdinWillSerialize(member, serializeUnityFields, policy ?? UnityPolicy);
+ result.HasCalculatedSerializeUnityFieldsFalseResult = true;
+ }
+
+ cacheForPolicy.Add(member, result);
+ }
+ else
+ {
+ if (serializeUnityFields && !result.HasCalculatedSerializeUnityFieldsTrueResult)
+ {
+ result.SerializeUnityFieldsTrueResult = CalculateOdinWillSerialize(member, serializeUnityFields, policy ?? UnityPolicy);
+ result.HasCalculatedSerializeUnityFieldsTrueResult = true;
+
+ cacheForPolicy[member] = result;
+ }
+ else if (!serializeUnityFields && !result.HasCalculatedSerializeUnityFieldsFalseResult)
+ {
+ result.SerializeUnityFieldsFalseResult = CalculateOdinWillSerialize(member, serializeUnityFields, policy ?? UnityPolicy);
+ result.HasCalculatedSerializeUnityFieldsFalseResult = true;
+
+ cacheForPolicy[member] = result;
+ }
+ }
+
+ return serializeUnityFields ? result.SerializeUnityFieldsTrueResult : result.SerializeUnityFieldsFalseResult;
+ }
+ }
+
+ private static bool CalculateOdinWillSerialize(MemberInfo member, bool serializeUnityFields, ISerializationPolicy policy)
+ {
+ if (member.DeclaringType == typeof(UnityEngine.Object)) return false;
+ if (!policy.ShouldSerializeMember(member)) return false;
+
+ // Allow serialization of fields with [OdinSerialize], regardless of whether Unity
+ // serializes the field or not
+ if (member is FieldInfo && member.IsDefined(typeof(OdinSerializeAttribute), true))
+ {
+ return true;
+ }
+
+ // No need to check whether Unity serializes it or not, our answer will always be the same
+ if (serializeUnityFields) return true;
+
+ try
+ {
+ if (SerializeReferenceAttributeType != null && member.IsDefined(SerializeReferenceAttributeType, true))
+ {
+ // Unity is serializing it as a polymorphic value
+ return false;
+ }
+ }
+ catch { }
+
+ if (GuessIfUnityWillSerialize(member)) return false;
+
+ return true;
+ }
+
+ private struct CachedSerializationBackendResult
+ {
+ public bool HasCalculatedSerializeUnityFieldsTrueResult;
+ public bool HasCalculatedSerializeUnityFieldsFalseResult;
+
+ public bool SerializeUnityFieldsTrueResult;
+ public bool SerializeUnityFieldsFalseResult;
+ }
+
+ private static readonly ISerializationPolicy UnityPolicy = SerializationPolicies.Unity;
+ private static readonly ISerializationPolicy EverythingPolicy = SerializationPolicies.Everything;
+ private static readonly ISerializationPolicy StrictPolicy = SerializationPolicies.Strict;
+ private static readonly Dictionary<MemberInfo, CachedSerializationBackendResult> OdinWillSerializeCache_UnityPolicy = new Dictionary<MemberInfo, CachedSerializationBackendResult>(ReferenceEqualityComparer<MemberInfo>.Default);
+ private static readonly Dictionary<MemberInfo, CachedSerializationBackendResult> OdinWillSerializeCache_EverythingPolicy = new Dictionary<MemberInfo, CachedSerializationBackendResult>(ReferenceEqualityComparer<MemberInfo>.Default);
+ private static readonly Dictionary<MemberInfo, CachedSerializationBackendResult> OdinWillSerializeCache_StrictPolicy = new Dictionary<MemberInfo, CachedSerializationBackendResult>(ReferenceEqualityComparer<MemberInfo>.Default);
+ private static readonly Dictionary<ISerializationPolicy, Dictionary<MemberInfo, CachedSerializationBackendResult>> OdinWillSerializeCache_CustomPolicies = new Dictionary<ISerializationPolicy, Dictionary<MemberInfo, CachedSerializationBackendResult>>(ReferenceEqualityComparer<ISerializationPolicy>.Default);
+
+ /// <summary>
+ /// Guesses whether or not Unity will serialize a given member. This is not completely accurate.
+ /// </summary>
+ /// <param name="member">The member to check.</param>
+ /// <returns>True if it is guessed that Unity will serialize the member, otherwise false.</returns>
+ /// <exception cref="System.ArgumentNullException">The parameter <paramref name="member"/> is null.</exception>
+ public static bool GuessIfUnityWillSerialize(MemberInfo member)
+ {
+ if (member == null)
+ {
+ throw new ArgumentNullException("member");
+ }
+
+ bool result;
+
+ lock (UnityWillSerializeMembersCache)
+ {
+ if (!UnityWillSerializeMembersCache.TryGetValue(member, out result))
+ {
+ result = GuessIfUnityWillSerializePrivate(member);
+ UnityWillSerializeMembersCache[member] = result;
+ }
+ }
+
+ return result;
+ }
+
+ private static bool GuessIfUnityWillSerializePrivate(MemberInfo member)
+ {
+ FieldInfo fieldInfo = member as FieldInfo;
+
+ if (fieldInfo == null || fieldInfo.IsStatic || fieldInfo.IsInitOnly)
+ {
+ return false;
+ }
+
+ if (fieldInfo.IsDefined<NonSerializedAttribute>())
+ {
+ return false;
+ }
+
+ if (SerializeReferenceAttributeType != null && fieldInfo.IsDefined(SerializeReferenceAttributeType, true))
+ {
+ return true;
+ }
+
+ if (!typeof(UnityEngine.Object).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.FieldType == fieldInfo.DeclaringType)
+ {
+ // Unity will not serialize references that are obviously cyclical
+ return false;
+ }
+
+ if (!(fieldInfo.IsPublic || fieldInfo.IsDefined<SerializeField>()))
+ {
+ return false;
+ }
+
+ if (fieldInfo.IsDefined<FixedBufferAttribute>())
+ {
+ return UnityVersion.IsVersionOrGreater(2017, 1);
+ }
+
+ return GuessIfUnityWillSerialize(fieldInfo.FieldType);
+ }
+
+ /// <summary>
+ /// Guesses whether or not Unity will serialize a given type. This is not completely accurate.
+ /// </summary>
+ /// <param name="type">The type to check.</param>
+ /// <returns>True if it is guessed that Unity will serialize the type, otherwise false.</returns>
+ /// <exception cref="System.ArgumentNullException">The parameter <paramref name="type"/> is null.</exception>
+ public static bool GuessIfUnityWillSerialize(Type type)
+ {
+ if (type == null)
+ {
+ throw new ArgumentNullException("type");
+ }
+
+ bool result;
+
+ lock (UnityWillSerializeTypesCache)
+ {
+ if (!UnityWillSerializeTypesCache.TryGetValue(type, out result))
+ {
+ result = GuessIfUnityWillSerializePrivate(type);
+ UnityWillSerializeTypesCache[type] = result;
+ }
+ }
+
+ return result;
+ }
+
+ private static bool GuessIfUnityWillSerializePrivate(Type type)
+ {
+ if (UnityNeverSerializesTypes.Contains(type) || UnityNeverSerializesTypeNames.Contains(type.FullName))
+ {
+ return false;
+ }
+
+ if (typeof(UnityEngine.Object).IsAssignableFrom(type))
+ {
+ if (type.IsGenericType)
+ {
+ return UnityVersion.IsVersionOrGreater(2020, 1);
+ }
+
+ // Unity will always serialize all of its own special objects
+ // Except when they have generic type arguments.
+ return true;
+ }
+
+ if (type.IsAbstract || type.IsInterface || type == typeof(object))
+ {
+ return false;
+ }
+
+ if (type.IsEnum)
+ {
+ var underlyingType = Enum.GetUnderlyingType(type);
+
+ if (UnityVersion.IsVersionOrGreater(5, 6))
+ {
+ return underlyingType != typeof(long) && underlyingType != typeof(ulong);
+ }
+ else
+ {
+ return underlyingType == typeof(int) || underlyingType == typeof(byte);
+ }
+ }
+
+ if (type.IsPrimitive || type == typeof(string))
+ {
+ return true;
+ }
+
+ if (typeof(Delegate).IsAssignableFrom(type))
+ {
+ return false;
+ }
+
+ if (typeof(UnityEventBase).IsAssignableFrom(type))
+ {
+ if (type.IsGenericType && !UnityVersion.IsVersionOrGreater(2020, 1))
+ {
+ return false;
+ }
+
+ return (type == typeof(UnityEvent) || type.IsDefined<SerializableAttribute>(false));
+ }
+
+ if (type.IsArray)
+ {
+ // Unity does not support multidim arrays, or arrays of lists or arrays.
+ var elementType = type.GetElementType();
+
+ return type.GetArrayRank() == 1
+ && !elementType.IsArray
+ && !elementType.ImplementsOpenGenericClass(typeof(List<>))
+ && GuessIfUnityWillSerialize(elementType);
+ }
+
+ if (type.IsGenericType && !type.IsGenericTypeDefinition && type.GetGenericTypeDefinition() == typeof(List<>))
+ {
+ // Unity does not support lists or arrays in lists.
+ var elementType = type.GetArgumentsOfInheritedOpenGenericClass(typeof(List<>))[0];
+ if (elementType.IsArray || elementType.ImplementsOpenGenericClass(typeof(List<>)))
+ {
+ return false;
+ }
+ return GuessIfUnityWillSerialize(elementType);
+ }
+
+ if (type.Assembly.FullName.StartsWith("UnityEngine", StringComparison.InvariantCulture) || type.Assembly.FullName.StartsWith("UnityEditor", StringComparison.InvariantCulture))
+ {
+ // We assume Unity will serialize all of their own structs and classes (many of them are not marked serializable).
+ // If not, well, too bad - the user can use the [OdinSerialize] attribute on their field/property in that case to trigger custom serialization.
+ return true;
+ }
+
+ // Unity 2020.1 added support for serializing arbitrary generic types directly
+ if (type.IsGenericType && !UnityVersion.IsVersionOrGreater(2020, 1))
+ {
+ return false;
+ }
+
+ // Unity does not serialize [Serializable] structs and classes if they are defined in mscorlib, System.dll or System.Core.dll if those are present
+ // Checking against the assemblies that declare System.String, HashSet<T> and LinkedList<T> is a simple way to do this.
+ if (type.Assembly == String_Assembly || type.Assembly == HashSet_Assembly || type.Assembly == LinkedList_Assembly)
+ {
+ return false;
+ }
+
+ if (type.IsDefined<SerializableAttribute>(false))
+ {
+ // Before Unity 4.5, Unity did not support serialization of custom structs, only custom classes
+ if (UnityVersion.IsVersionOrGreater(4, 5))
+ {
+ return true;
+ }
+ else
+ {
+ return type.IsClass;
+ }
+ }
+
+ // Check for synclists if legacy networking is present
+ // it was removed in 2018.2
+ if (!UnityVersion.IsVersionOrGreater(2018, 2))
+ {
+ Type current = type.BaseType;
+
+ while (current != null && current != typeof(object))
+ {
+ if (current.IsGenericType && current.GetGenericTypeDefinition().FullName == "UnityEngine.Networking.SyncListStruct`1")
+ {
+ return true;
+ }
+
+ current = current.BaseType;
+ };
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void SerializeUnityObject(UnityEngine.Object unityObject, ref SerializationData data, bool serializeUnityFields = false, SerializationContext context = null)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+#if UNITY_EDITOR
+
+ if (OdinPrefabSerializationEditorUtility.HasNewPrefabWorkflow)
+ {
+ ISupportsPrefabSerialization supporter = unityObject as ISupportsPrefabSerialization;
+
+ if (supporter != null)
+ {
+ var sData = supporter.SerializationData;
+
+ //if (!sData.ContainsData)
+ //{
+ // return;
+ //}
+
+ sData.Prefab = null;
+ supporter.SerializationData = sData;
+ }
+ }
+
+ {
+ bool pretendIsPlayer = Application.isPlaying && !UnityEditor.AssetDatabase.Contains(unityObject);
+
+ //
+ // Look through a stack trace to determine some things about the current serialization context.
+ // For example, we check if we are currently building a player, or if we are currently recording prefab instance property modifications.
+ // This is pretty hacky, but as far as we can tell it's the only way to do it.
+ //
+
+ {
+ var stackFrames = new System.Diagnostics.StackTrace().GetFrames();
+ Type buildPipelineType = typeof(UnityEditor.BuildPipeline);
+ Type prefabUtilityType = typeof(UnityEditor.PrefabUtility);
+
+
+ for (int i = 0; i < stackFrames.Length; i++)
+ {
+ var frame = stackFrames[i];
+ var method = frame.GetMethod();
+
+ if (method.DeclaringType == buildPipelineType || method.DeclaringType == SBP_ContentPipelineType)
+ {
+ // We are currently building a player
+ pretendIsPlayer = true;
+ break;
+ }
+
+ if (method.DeclaringType == prefabUtilityType && method.Name == "RecordPrefabInstancePropertyModifications")
+ {
+ // Do nothing whatsoever and return immediately, lest we break Unity's "smart" modification recording
+ return;
+ }
+ }
+ }
+
+ if (ForceEditorModeSerialization)
+ {
+ pretendIsPlayer = false;
+ }
+
+ //
+ // Prefab handling
+ //
+ // If we're not building a player and the Unity object is a prefab instance
+ // that supports special prefab serialization, we enter a special bail-out case.
+ //
+
+ if (!pretendIsPlayer)
+ {
+ UnityEngine.Object prefab = null;
+ SerializationData prefabData = default(SerializationData);
+
+ bool prefabDataIsFromSelf = false;
+
+ if (OdinPrefabSerializationEditorUtility.ObjectIsPrefabInstance(unityObject))
+ {
+ prefab = OdinPrefabSerializationEditorUtility.GetCorrespondingObjectFromSource(unityObject);
+
+ if (prefab.SafeIsUnityNull() && !object.ReferenceEquals(data.Prefab, null))
+ {
+ // Sometimes, GetPrefabParent does not return the prefab,
+ // because Unity is just completely unreliable.
+ //
+ // In these cases, we sometimes have a reference to the
+ // prefab in the data. If so, we can use that instead.
+ //
+ // Even though that reference is "fake null".
+
+ prefab = data.Prefab;
+ }
+
+ if (!object.ReferenceEquals(prefab, null))
+ {
+ if (prefab is ISupportsPrefabSerialization)
+ {
+ var pData = (prefab as ISupportsPrefabSerialization).SerializationData;
+
+ if (pData.ContainsData)
+ {
+ prefabData = pData;
+ }
+ else
+ {
+ prefabData = data;
+ prefabData.Prefab = null;
+ prefabDataIsFromSelf = true;
+ }
+ }
+ else if (prefab.GetType() != typeof(UnityEngine.Object))
+ {
+ //Debug.LogWarning(unityObject.name + " is a prefab instance, but the prefab reference type " + prefab.GetType().GetNiceName() + " does not implement the interface " + typeof(ISupportsPrefabSerialization).GetNiceName() + "; non-Unity-serialized data will most likely not be updated properly from the prefab any more.");
+ //prefab = null;
+
+ prefabData = data;
+ prefabData.Prefab = null;
+ prefabDataIsFromSelf = true;
+ }
+ }
+ }
+
+ if (!object.ReferenceEquals(prefab, null))
+ {
+ // We will bail out. But first...
+
+ if (!prefabDataIsFromSelf && prefabData.PrefabModifications != null && prefabData.PrefabModifications.Count > 0)
+ {
+ //
+ // This is a special case that can happen after changes to a prefab instance
+ // have been applied to the source prefab using "Apply Changes", thus copying
+ // the instances' applied changes over to the source prefab.
+ //
+ // We re-serialize the prefab, to make sure its data is properly saved.
+ // Though data saved this way will still work, it is quite inefficient.
+ //
+
+ try
+ {
+ (prefab as ISerializationCallbackReceiver).OnBeforeSerialize();
+ }
+ catch (Exception ex)
+ {
+ // This can sometimes throw null reference exceptions in the new prefab workflow,
+ // if people are doing nested stuff despite the fac that they really, really shouldn't.
+ //
+ // Just ignore it.
+ if (!OdinPrefabSerializationEditorUtility.HasNewPrefabWorkflow)
+ {
+ throw ex;
+ }
+ }
+
+ EditorApplication_delayCall_Alias += () =>
+ {
+ if (prefab)
+ {
+ UnityEditor.EditorUtility.SetDirty(prefab);
+ //UnityEditor.AssetDatabase.SaveAssets(); // Has a tendency to cause infinite serialization loops
+ }
+ };
+
+ prefabData = (prefab as ISupportsPrefabSerialization).SerializationData;
+ }
+
+ // Now we determine the modifications string to keep
+
+ bool newModifications = false;
+ List<string> modificationsToKeep;
+ List<PrefabModification> modificationsList;
+ List<UnityEngine.Object> modificationsReferencedUnityObjects = data.PrefabModificationsReferencedUnityObjects;
+
+ if (RegisteredPrefabModifications.TryGetValue(unityObject, out modificationsList))
+ {
+ RegisteredPrefabModifications.Remove(unityObject);
+
+ // We have to generate a new prefab modification string from the registered changes
+ modificationsToKeep = SerializePrefabModifications(modificationsList, ref modificationsReferencedUnityObjects);
+
+#if PREFAB_DEBUG
+ Debug.Log("Setting new modifications: ", unityObject);
+
+ foreach (var mod in modificationsToKeep)
+ {
+ Debug.Log(" " + mod);
+ }
+#endif
+
+ newModifications = true;
+ }
+ else
+ {
+ // Keep the old ones
+ modificationsToKeep = data.PrefabModifications;
+ }
+
+ // Make sure we have the same base data as the prefab (except UnityObject references), then change the rest
+ var unityObjects = data.ReferencedUnityObjects;
+
+ data = prefabData;
+ data.ReferencedUnityObjects = unityObjects;
+
+ //if (unityObjects.Count == prefabData.ReferencedUnityObjects.Count)
+ //{
+ //}
+ //else
+ //{
+ // var stackTrace = new System.Diagnostics.StackTrace();
+
+ // //DefaultLoggers.DefaultLogger.LogError(
+ // // "Prefab instance serialization error on object'" + unityObject.name + "': Unity object reference count mismatch " +
+ // // "between prefab and prefab instance in the core umodified data! This should never, ever happen! Prefab instance " +
+ // // "references have been replaced with the prefab's references! Unity object references may have been changed from " +
+ // // "expected values! Please report this error and how to reproduce it at 'http://bitbucket.org/sirenix/odin-inspector/issues'.");
+ //}
+
+ data.Prefab = prefab;
+ data.PrefabModifications = modificationsToKeep;
+ data.PrefabModificationsReferencedUnityObjects = modificationsReferencedUnityObjects;
+
+ if (newModifications)
+ {
+ SetUnityObjectModifications(unityObject, ref data, prefab);
+ }
+
+ // Now we determine the Unity object references to keep if this prefab instance is ever applied
+ if (data.Prefab != null) // It can still be "fake null", in which case, never mind
+ {
+ PrefabDeserializeUtility.CleanSceneObjectToKeepOnApply();
+
+ lock (PrefabDeserializeUtility.DeserializePrefabs_LOCK)
+ {
+ HashSet<object> keep = PrefabDeserializeUtility.GetSceneObjectsToKeepSet(unityObject, true);
+ keep.Clear();
+
+ if (data.PrefabModificationsReferencedUnityObjects != null && data.PrefabModificationsReferencedUnityObjects.Count > 0)
+ {
+ //var prefabRoot = UnityEditor.PrefabUtility.FindPrefabRoot(((Component)data.Prefab).gameObject);
+ var instanceRoot = UnityEditor.PrefabUtility.FindPrefabRoot(((Component)unityObject).gameObject);
+
+ foreach (var reference in data.PrefabModificationsReferencedUnityObjects)
+ {
+ if (reference == null) continue;
+ if (!(reference is GameObject || reference is Component)) continue;
+ if (UnityEditor.AssetDatabase.Contains(reference)) continue;
+
+ var referencePrefabType = UnityEditor.PrefabUtility.GetPrefabType(reference);
+
+ bool mightBeInPrefab = referencePrefabType == UnityEditor.PrefabType.Prefab
+ || referencePrefabType == UnityEditor.PrefabType.PrefabInstance
+ || referencePrefabType == UnityEditor.PrefabType.ModelPrefab
+ || referencePrefabType == UnityEditor.PrefabType.ModelPrefabInstance;
+
+ if (!mightBeInPrefab)
+ {
+ if (PrefabUtility_IsComponentAddedToPrefabInstance_MethodInfo != null)
+ {
+ if (reference is Component && (bool)PrefabUtility_IsComponentAddedToPrefabInstance_MethodInfo.Invoke(null, new object[] { reference }))
+ {
+ mightBeInPrefab = true;
+ }
+ }
+ }
+
+ if (!mightBeInPrefab)
+ {
+ keep.Add(reference);
+ continue;
+ }
+
+ var gameObject = (GameObject)(reference is GameObject ? reference : (reference as Component).gameObject);
+ var referenceRoot = UnityEditor.PrefabUtility.FindPrefabRoot(gameObject);
+
+ if (referenceRoot != instanceRoot)
+ {
+ keep.Add(reference);
+ }
+ }
+ }
+ }
+ }
+
+ return; // Buh bye
+ }
+ }
+
+ //
+ // We are not dealing with a properly supported prefab instance if we get this far.
+ // Serialize as if it isn't a prefab instance.
+ //
+
+ // Ensure there is no superfluous data left over after serialization
+ // (We will reassign all necessary data.)
+ data.Reset();
+
+ DataFormat format;
+
+ // Get the format to serialize as
+ {
+ IOverridesSerializationFormat formatOverride = unityObject as IOverridesSerializationFormat;
+
+ if (formatOverride != null)
+ {
+ format = formatOverride.GetFormatToSerializeAs(pretendIsPlayer);
+ }
+ else if (GlobalSerializationConfig.HasInstanceLoaded)
+ {
+ if (pretendIsPlayer)
+ {
+ format = GlobalSerializationConfig.Instance.BuildSerializationFormat;
+ }
+ else
+ {
+ format = GlobalSerializationConfig.Instance.EditorSerializationFormat;
+ }
+ }
+ else if (pretendIsPlayer)
+ {
+ format = DataFormat.Binary;
+ }
+ else
+ {
+ format = DataFormat.Nodes;
+ }
+ }
+
+ ISerializationPolicy serializationPolicy = SerializationPolicies.Unity;
+
+ // Get the policy to serialize with
+ {
+ IOverridesSerializationPolicy policyOverride = unityObject as IOverridesSerializationPolicy;
+
+ if (policyOverride != null)
+ {
+ serializationPolicy = policyOverride.SerializationPolicy ?? SerializationPolicies.Unity;
+
+ if (context != null)
+ {
+ context.Config.SerializationPolicy = serializationPolicy;
+ }
+
+ serializeUnityFields = policyOverride.OdinSerializesUnityFields;
+ }
+
+ }
+
+ if (pretendIsPlayer)
+ {
+ // We pretend as though we're serializing outside of the editor
+ if (format == DataFormat.Nodes)
+ {
+ Debug.LogWarning("The serialization format '" + format.ToString() + "' is disabled in play mode, and when building a player. Defaulting to the format '" + DataFormat.Binary.ToString() + "' instead.");
+ format = DataFormat.Binary;
+ }
+
+ UnitySerializationUtility.SerializeUnityObject(unityObject, ref data.SerializedBytes, ref data.ReferencedUnityObjects, format, serializeUnityFields, context);
+ data.SerializedFormat = format;
+ }
+ else
+ {
+ if (format == DataFormat.Nodes)
+ {
+ // Special case for node format
+ if (context == null)
+ {
+ using (var newContext = Cache<SerializationContext>.Claim())
+ using (var writer = new SerializationNodeDataWriter(newContext))
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ if (data.SerializationNodes != null)
+ {
+ // Reuse pre-expanded list to keep GC down
+ data.SerializationNodes.Clear();
+ writer.Nodes = data.SerializationNodes;
+ }
+
+ resolver.Value.SetReferencedUnityObjects(data.ReferencedUnityObjects);
+
+ newContext.Value.Config.SerializationPolicy = serializationPolicy;
+ newContext.Value.IndexReferenceResolver = resolver.Value;
+
+ writer.Context = newContext;
+
+ UnitySerializationUtility.SerializeUnityObject(unityObject, writer, serializeUnityFields);
+ data.SerializationNodes = writer.Nodes;
+ data.ReferencedUnityObjects = resolver.Value.GetReferencedUnityObjects();
+ }
+ }
+ else
+ {
+ using (var writer = new SerializationNodeDataWriter(context))
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ if (data.SerializationNodes != null)
+ {
+ // Reuse pre-expanded list to keep GC down
+ data.SerializationNodes.Clear();
+ writer.Nodes = data.SerializationNodes;
+ }
+
+ resolver.Value.SetReferencedUnityObjects(data.ReferencedUnityObjects);
+ context.IndexReferenceResolver = resolver.Value;
+
+ UnitySerializationUtility.SerializeUnityObject(unityObject, writer, serializeUnityFields);
+ data.SerializationNodes = writer.Nodes;
+ data.ReferencedUnityObjects = resolver.Value.GetReferencedUnityObjects();
+ }
+ }
+ }
+ else
+ {
+ UnitySerializationUtility.SerializeUnityObject(unityObject, ref data.SerializedBytesString, ref data.ReferencedUnityObjects, format, serializeUnityFields, context);
+ }
+
+ data.SerializedFormat = format;
+ }
+ }
+#else
+ {
+ DataFormat format;
+ IOverridesSerializationFormat formatOverride = unityObject as IOverridesSerializationFormat;
+
+ if (formatOverride != null)
+ {
+ format = formatOverride.GetFormatToSerializeAs(true);
+ }
+ else if (GlobalSerializationConfig.HasInstanceLoaded)
+ {
+ format = GlobalSerializationConfig.Instance.BuildSerializationFormat;
+ }
+ else
+ {
+ format = DataFormat.Binary;
+ }
+
+ if (format == DataFormat.Nodes)
+ {
+ Debug.LogWarning("The serialization format '" + format.ToString() + "' is disabled outside of the editor. Defaulting to the format '" + DataFormat.Binary.ToString() + "' instead.");
+ format = DataFormat.Binary;
+ }
+
+ UnitySerializationUtility.SerializeUnityObject(unityObject, ref data.SerializedBytes, ref data.ReferencedUnityObjects, format);
+ data.SerializedFormat = format;
+ }
+#endif
+ }
+
+#if UNITY_EDITOR
+
+ private static void SetUnityObjectModifications(UnityEngine.Object unityObject, ref SerializationData data, UnityEngine.Object prefab)
+ {
+ //
+ // We need to set the modifications to the prefab instance manually,
+ // to ensure that Unity gets it right and doesn't mess with them.
+ //
+
+ Type unityObjectType = unityObject.GetType();
+ var serializedDataField = unityObjectType.GetAllMembers<FieldInfo>(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
+ .Where(field => field.FieldType == typeof(SerializationData) && UnitySerializationUtility.GuessIfUnityWillSerialize(field))
+ .LastOrDefault();
+
+ if (serializedDataField == null)
+ {
+ Debug.LogError("Could not find a field of type " + typeof(SerializationData).Name + " on the serializing type " + unityObjectType.GetNiceName() + " when trying to manually set prefab modifications. It is possible that prefab instances of this type will be corrupted if changes are ever applied to prefab.", prefab);
+ }
+ else
+ {
+ string serializedDataPath = serializedDataField.Name + ".";
+ string referencedUnityObjectsPath = serializedDataPath + SerializationData.PrefabModificationsReferencedUnityObjectsFieldName + ".Array.";
+ string modificationsPath = serializedDataPath + SerializationData.PrefabModificationsFieldName + ".Array.";
+ string prefabPath = serializedDataPath + SerializationData.PrefabFieldName;
+
+ var mods = UnityEditor.PrefabUtility.GetPropertyModifications(unityObject).ToList();
+
+ //
+ // Clear all old modifications to serialized data out
+ //
+
+ for (int i = 0; i < mods.Count; i++)
+ {
+ var mod = mods[i];
+
+ if (mod.propertyPath.StartsWith(serializedDataPath, StringComparison.InvariantCulture) && object.ReferenceEquals(mod.target, prefab))
+ {
+ mods.RemoveAt(i);
+ i--;
+ }
+ }
+
+ //
+ // Add the new modifications
+ //
+
+ // Array length changes seem to always come first? Let's do that to be sure...
+ mods.Insert(0, new UnityEditor.PropertyModification()
+ {
+ target = prefab,
+ propertyPath = referencedUnityObjectsPath + "size",
+ value = data.PrefabModificationsReferencedUnityObjects.Count.ToString("D", CultureInfo.InvariantCulture)
+ });
+
+ mods.Insert(0, new UnityEditor.PropertyModification()
+ {
+ target = prefab,
+ propertyPath = modificationsPath + "size",
+ value = data.PrefabModifications.Count.ToString("D", CultureInfo.InvariantCulture)
+ });
+
+ // Then the prefab object reference
+ mods.Add(new UnityEditor.PropertyModification()
+ {
+ target = prefab,
+ propertyPath = prefabPath,
+ objectReference = prefab
+ });
+
+ // Then the actual array values
+ for (int i = 0; i < data.PrefabModificationsReferencedUnityObjects.Count; i++)
+ {
+ mods.Add(new UnityEditor.PropertyModification()
+ {
+ target = prefab,
+ propertyPath = referencedUnityObjectsPath + "data[" + i.ToString("D", CultureInfo.InvariantCulture) + "]",
+ objectReference = data.PrefabModificationsReferencedUnityObjects[i]
+ });
+ }
+
+ for (int i = 0; i < data.PrefabModifications.Count; i++)
+ {
+ mods.Add(new UnityEditor.PropertyModification()
+ {
+ target = prefab,
+ propertyPath = modificationsPath + "data[" + i.ToString("D", CultureInfo.InvariantCulture) + "]",
+ value = data.PrefabModifications[i]
+ });
+ }
+
+ // Set the Unity property modifications
+
+ // This won't always stick; there is code in the PropertyTree class
+ // that keeps checking if the number of custom modifications is correct
+ // and, if not, it keeps registering the change until Unity gets it.
+
+ // Setting the prefab modifications here directly has a tendency to crash the Unity Editor, so we use a delayed call
+ // so the modifications are set during a time that's better for Unity.
+ EditorApplication_delayCall_Alias += () =>
+ {
+#if PREFAB_DEBUG
+ Debug.Log("DELAYED: Actually setting prefab modifications:");
+ foreach (var mod in mods)
+ {
+ if (!mod.propertyPath.StartsWith("serializationData")) continue;
+
+ int index = -1;
+
+ if (mod.target is Component)
+ {
+ Component com = mod.target as Component;
+
+ var coms = com.gameObject.GetComponents(com.GetType());
+
+ for (int j = 0; j < coms.Length; j++)
+ {
+ if (object.ReferenceEquals(coms[j], mod.target))
+ {
+ index = j;
+ break;
+ }
+ }
+ }
+
+ Debug.Log(" " + mod.target.name + " (" + index + ") " + mod.propertyPath + ": " + mod.value);
+ }
+#endif
+
+ UnityObjectsWaitingForDelayedModificationApply.Remove(unityObject);
+ UnityEditor.PrefabUtility.SetPropertyModifications(unityObject, mods.ToArray());
+ };
+
+ UnityObjectsWaitingForDelayedModificationApply.Add(unityObject);
+ }
+ }
+
+#endif
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void SerializeUnityObject(UnityEngine.Object unityObject, ref string base64Bytes, ref List<UnityEngine.Object> referencedUnityObjects, DataFormat format, bool serializeUnityFields = false, SerializationContext context = null)
+ {
+ byte[] bytes = null;
+ SerializeUnityObject(unityObject, ref bytes, ref referencedUnityObjects, format, serializeUnityFields, context);
+ base64Bytes = Convert.ToBase64String(bytes);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void SerializeUnityObject(UnityEngine.Object unityObject, ref byte[] bytes, ref List<UnityEngine.Object> referencedUnityObjects, DataFormat format, bool serializeUnityFields = false, SerializationContext context = null)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (format == DataFormat.Nodes)
+ {
+ Debug.LogError("The serialization data format '" + format.ToString() + "' is not supported by this method. You must create your own writer.");
+ return;
+ }
+
+ if (referencedUnityObjects == null)
+ {
+ referencedUnityObjects = new List<UnityEngine.Object>();
+ }
+ else
+ {
+ referencedUnityObjects.Clear();
+ }
+
+ using (var stream = Cache<CachedMemoryStream>.Claim())
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ resolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
+
+ if (context != null)
+ {
+ context.IndexReferenceResolver = resolver.Value;
+ using (var writerCache = GetCachedUnityWriter(format, stream.Value.MemoryStream, context))
+ {
+ SerializeUnityObject(unityObject, writerCache.Value as IDataWriter, serializeUnityFields);
+ }
+ }
+ else
+ {
+ using (var con = Cache<SerializationContext>.Claim())
+ {
+ con.Value.Config.SerializationPolicy = SerializationPolicies.Unity;
+
+ /* If the config instance is not loaded (it should usually be, but in rare cases
+ * it's not), we must not ask for it, as we are not allowed to load from resources
+ * or the asset database during some serialization callbacks.
+ *
+ * (Trying to do that causes internal Unity errors and potentially even crashes.)
+ *
+ * If it's not loaded, we fall back to default values, since there's no other choice.
+ */
+ if (GlobalSerializationConfig.HasInstanceLoaded)
+ {
+ //Debug.Log("Serializing " + unityObject.GetType().Name + " WITH loaded!");
+ con.Value.Config.DebugContext.ErrorHandlingPolicy = GlobalSerializationConfig.Instance.ErrorHandlingPolicy;
+ con.Value.Config.DebugContext.LoggingPolicy = GlobalSerializationConfig.Instance.LoggingPolicy;
+ con.Value.Config.DebugContext.Logger = GlobalSerializationConfig.Instance.Logger;
+ }
+ else
+ {
+ //Debug.Log("Serializing " + unityObject.GetType().Name + " WITHOUT loaded!");
+ con.Value.Config.DebugContext.ErrorHandlingPolicy = ErrorHandlingPolicy.Resilient;
+ con.Value.Config.DebugContext.LoggingPolicy = LoggingPolicy.LogErrors;
+ con.Value.Config.DebugContext.Logger = DefaultLoggers.UnityLogger;
+ }
+
+ con.Value.IndexReferenceResolver = resolver.Value;
+
+ using (var writerCache = GetCachedUnityWriter(format, stream.Value.MemoryStream, con))
+ {
+ SerializeUnityObject(unityObject, writerCache.Value as IDataWriter, serializeUnityFields);
+ }
+ }
+ }
+
+ bytes = stream.Value.MemoryStream.ToArray();
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void SerializeUnityObject(UnityEngine.Object unityObject, IDataWriter writer, bool serializeUnityFields = false)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (writer == null)
+ {
+ throw new ArgumentNullException("writer");
+ }
+
+ //if (unityObject is Component)
+ //{
+ // Component com = (Component)unityObject;
+
+ // if (com.gameObject.scene.IsValid())
+ // {
+ // Debug.Log("Serializing scene " + com.gameObject.scene.name + ": " + com, com);
+ // }
+ // else
+ // {
+ // var path = UnityEditor.AssetDatabase.GetAssetPath(com);
+
+ // Debug.Log("Serializing prefab '" + path + "': " + com, com);
+ // }
+ //}
+ //else
+ //{
+ // Debug.Log("Serializing " + unityObject, unityObject);
+ //}
+
+ try
+ {
+ writer.PrepareNewSerializationSession();
+
+ var members = FormatterUtilities.GetSerializableMembers(unityObject.GetType(), writer.Context.Config.SerializationPolicy);
+ object unityObjectInstance = unityObject;
+
+ for (int i = 0; i < members.Length; i++)
+ {
+ var member = members[i];
+ WeakValueGetter getter = null;
+
+ if (!OdinWillSerialize(member, serializeUnityFields, writer.Context.Config.SerializationPolicy) || (getter = GetCachedUnityMemberGetter(member)) == null)
+ {
+ continue;
+ }
+
+ var value = getter(ref unityObjectInstance);
+
+ bool isNull = object.ReferenceEquals(value, null);
+
+ // Never serialize serialization data. That way lies madness.
+ if (!isNull && value.GetType() == typeof(SerializationData))
+ {
+ continue;
+ }
+
+ Serializer serializer = Serializer.Get(FormatterUtilities.GetContainedType(member));
+
+ try
+ {
+ serializer.WriteValueWeak(member.Name, value, writer);
+ }
+ catch (Exception ex)
+ {
+ writer.Context.Config.DebugContext.LogException(ex);
+ }
+ }
+
+ writer.FlushToStream();
+ }
+ catch (SerializationAbortException ex)
+ {
+ throw new SerializationAbortException("Serialization of type '" + unityObject.GetType().GetNiceFullName() + "' aborted.", ex);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(new Exception("Exception thrown while serializing type '" + unityObject.GetType().GetNiceFullName() + "': " + ex.Message, ex));
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void DeserializeUnityObject(UnityEngine.Object unityObject, ref SerializationData data, DeserializationContext context = null)
+ {
+ //#if UNITY_EDITOR
+ DeserializeUnityObject(unityObject, ref data, context, isPrefabData: false, prefabInstanceUnityObjects: null);
+ //#else
+ // UnitySerializationUtility.DeserializeUnityObject(unityObject, ref data.SerializedBytes, ref data.ReferencedUnityObjects, data.SerializedFormat, context);
+ // data = default(SerializationData); // Free all data for GC
+ //#endif
+ }
+
+ private static void DeserializeUnityObject(UnityEngine.Object unityObject, ref SerializationData data, DeserializationContext context, bool isPrefabData, List<UnityEngine.Object> prefabInstanceUnityObjects)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (isPrefabData && prefabInstanceUnityObjects == null)
+ {
+ prefabInstanceUnityObjects = new List<UnityEngine.Object>(); // There's likely no data at all
+ }
+
+#if UNITY_EDITOR
+ if (OdinPrefabSerializationEditorUtility.HasNewPrefabWorkflow)
+ {
+ ISupportsPrefabSerialization supporter = unityObject as ISupportsPrefabSerialization;
+
+ if (supporter != null)
+ {
+ var sData = supporter.SerializationData;
+
+ if (!sData.ContainsData)
+ {
+ return;
+ }
+
+ sData.Prefab = null;
+ supporter.SerializationData = sData;
+ }
+ }
+
+ // TODO: This fix needs to be applied for edge-cases! But we also need a way to only do it while in the Editor! and if UNITY_EDITOR is not enough.
+ //Debug.Log("Deserializing" + new System.Diagnostics.StackTrace().ToString(), unityObject);
+ //var prefabDataObject = data.Prefab as ISupportsPrefabSerialization;
+ //if (prefabDataObject != null && data.PrefabModifications != null && UnityEditor.AssetDatabase.Contains(data.Prefab))
+ //{
+ // // In some rare cases, prefab instances can become corrupted if there somehow end up being applied bad prefab modifications
+ // // for the JSON, Nodes or Binary data fields. This will of course result in a corrupted data-stream, and if the resilient serialization mode fails
+ // // to be resilient, scenes will also give an error when opnened, and the object won't fix itself.
+ // // How these bad Unity prefab modifications ends up there in the first place, is a mystery.
+
+ // // ----------------
+ // // But it's easy enought to replicate:
+ // // 1. Create a game object with a SerializedMonoBehaviour containing a dictionary.
+ // // 2. Make it a prefab
+ // // 3. Instantiate the dictionary and add an entry to it in the prefab instance and hit apply (ONCE)
+ // // 4. Now totally random prefab modifications are applied to the SerializedNodes data member: https://jumpshare.com/v/eiZv39CUJKOuNCVhMu83
+ // // - These are harmless in many situations, as the values are exactly the same as in the prefab: https://jumpshare.com/v/aMlA0dD7gsA2uvO5ot48
+ // // - Furthermore, when hitting apply again, all of the bad modifications go away.
+ // // 5. But if we instead of hitting apply again, save the scene, and remove the dictionary member from the c# class,
+ // // we're now left with permanent modifications to the data.SerializaitionNodes, which will result
+ // // in a corrupted data stream, as we continue to modify the prefab it self.
+ // // 6. Set the serialization mode to log warnings and errors, remove the dictioanry from the c# class and open the scene again.
+ // // ----------------
+
+ // // Here we make sure that we deserialize the object using the data from the prefab, and override any potentially corrupted data.
+ // // In prefab instances, the only thing we care about are the actual prefab-modifications (data.PrefabModifications and data.PrefabModificationsReferencedUnityObjects)
+ // // This fix will also remove any corruped data, and trigger Unity to remove all bad prefab modifications.
+ // var prefabData = prefabDataObject.SerializationData;
+ // if (prefabData.ContainsData)
+ // {
+ // data.SerializedFormat = prefabData.SerializedFormat;
+ // data.SerializationNodes = prefabData.SerializationNodes ?? new List<SerializationNode>();
+ // data.ReferencedUnityObjects = prefabData.ReferencedUnityObjects ?? new List<UnityEngine.Object>();
+ // data.SerializedBytesString = prefabData.SerializedBytesString ?? "";
+ // data.SerializedBytes = prefabData.SerializedBytes ?? new byte[0];
+ // }
+ //}
+#endif
+
+ if ((data.SerializedBytes != null && data.SerializedBytes.Length > 0) && (data.SerializationNodes == null || data.SerializationNodes.Count == 0))
+ {
+ // If it happens that we have bytes in the serialized bytes array
+ // then we deserialize from that instead.
+
+ // This happens often in play mode, when instantiating, since we
+ // are emulating build behaviour.
+
+ if (data.SerializedFormat == DataFormat.Nodes)
+ {
+ // The stored format says nodes, but there is no serialized node data.
+ // Figure out what format the serialized bytes are in, and deserialize that format instead
+
+ DataFormat formatGuess = data.SerializedBytes[0] == '{' ? DataFormat.JSON : DataFormat.Binary;
+
+ try
+ {
+ var bytesStr = ProperBitConverter.BytesToHexString(data.SerializedBytes);
+ Debug.LogWarning("Serialization data has only bytes stored, but the serialized format is marked as being 'Nodes', which is incompatible with data stored as a byte array. Based on the appearance of the serialized bytes, Odin has guessed that the data format is '" + formatGuess + "', and will attempt to deserialize the bytes using that format. The serialized bytes follow, converted to a hex string: " + bytesStr);
+ }
+ catch { }
+
+ UnitySerializationUtility.DeserializeUnityObject(unityObject, ref data.SerializedBytes, ref data.ReferencedUnityObjects, formatGuess, context);
+ }
+ else
+ {
+ UnitySerializationUtility.DeserializeUnityObject(unityObject, ref data.SerializedBytes, ref data.ReferencedUnityObjects, data.SerializedFormat, context);
+ }
+
+ // If there are any prefab modifications, we should *always* apply those
+ ApplyPrefabModifications(unityObject, data.PrefabModifications, data.PrefabModificationsReferencedUnityObjects);
+ }
+ else
+ {
+ Cache<DeserializationContext> cachedContext = null;
+
+ try
+ {
+ if (context == null)
+ {
+ cachedContext = Cache<DeserializationContext>.Claim();
+ context = cachedContext;
+
+ context.Config.SerializationPolicy = SerializationPolicies.Unity;
+
+ /* If the config instance is not loaded (it should usually be, but in rare cases
+ * it's not), we must not ask for it, as we are not allowed to load from resources
+ * or the asset database during some serialization callbacks.
+ *
+ * (Trying to do that causes internal Unity errors and potentially even crashes.)
+ *
+ * If it's not loaded, we fall back to default values, since there's no other choice.
+ */
+ if (GlobalSerializationConfig.HasInstanceLoaded)
+ {
+ //Debug.Log("Deserializing " + unityObject.GetType().Name + " WITH loaded!");
+ context.Config.DebugContext.ErrorHandlingPolicy = GlobalSerializationConfig.Instance.ErrorHandlingPolicy;
+ context.Config.DebugContext.LoggingPolicy = GlobalSerializationConfig.Instance.LoggingPolicy;
+ context.Config.DebugContext.Logger = GlobalSerializationConfig.Instance.Logger;
+ }
+ else
+ {
+ //Debug.Log("Deserializing " + unityObject.GetType().Name + " WITHOUT loaded!");
+ context.Config.DebugContext.ErrorHandlingPolicy = ErrorHandlingPolicy.Resilient;
+ context.Config.DebugContext.LoggingPolicy = LoggingPolicy.LogErrors;
+ context.Config.DebugContext.Logger = DefaultLoggers.UnityLogger;
+ }
+ }
+
+ // If we have a policy override, use that
+ {
+ IOverridesSerializationPolicy policyOverride = unityObject as IOverridesSerializationPolicy;
+
+ if (policyOverride != null)
+ {
+ var serializationPolicy = policyOverride.SerializationPolicy;
+
+ if (serializationPolicy != null)
+ {
+ context.Config.SerializationPolicy = serializationPolicy;
+ }
+ }
+
+ }
+
+ if (!isPrefabData && !data.Prefab.SafeIsUnityNull())
+ {
+ if (data.Prefab is ISupportsPrefabSerialization)
+ {
+ if (object.ReferenceEquals(data.Prefab, unityObject) && data.PrefabModifications != null && data.PrefabModifications.Count > 0)
+ {
+ // We are deserializing a prefab, which has *just* had changes applied
+ // from an instance of itself.
+ //
+ // This is the only place, anywhere, where we can detect this happening
+ // so we need to register it, so the prefab instance that just applied
+ // its values knows to wipe all of its modifications clean.
+
+ // However, we only do this in the editor. If it happens outside of the
+ // editor it would be deeply strange, but we shouldn't correct anything
+ // in that case as it makes no sense.
+
+#if UNITY_EDITOR
+ lock (PrefabDeserializeUtility.DeserializePrefabs_LOCK)
+ {
+ PrefabDeserializeUtility.PrefabsWithValuesApplied.Add(unityObject);
+ }
+#endif
+ }
+ else
+ {
+ // We are dealing with a prefab instance, which is a special bail-out case
+ SerializationData prefabData = (data.Prefab as ISupportsPrefabSerialization).SerializationData;
+
+#if UNITY_EDITOR
+ lock (PrefabDeserializeUtility.DeserializePrefabs_LOCK)
+ {
+ // Only perform this check in the editor, as we are never dealing with a prefab
+ // instance outside of the editor - even if the serialized data is weird
+ if (PrefabDeserializeUtility.PrefabsWithValuesApplied.Contains(data.Prefab))
+ {
+ // Our prefab has had values applied; now to check if the object we're
+ // deserializing was the one to apply those values. If it is, then we
+ // have to wipe all of this object's prefab modifications clean.
+ //
+ // So far, the only way we know how to do that, is checking whether this
+ // object is currently selected.
+
+ if (PrefabSelectionTracker.IsCurrentlySelectedPrefabRoot(unityObject))
+ {
+ PrefabDeserializeUtility.PrefabsWithValuesApplied.Remove(data.Prefab);
+
+ List<PrefabModification> newModifications = null;
+ HashSet<object> keep = PrefabDeserializeUtility.GetSceneObjectsToKeepSet(unityObject, false);
+
+ if (data.PrefabModificationsReferencedUnityObjects.Count > 0 && keep != null && keep.Count > 0)
+ {
+ newModifications = DeserializePrefabModifications(data.PrefabModifications, data.PrefabModificationsReferencedUnityObjects);
+ newModifications.RemoveAll(n => object.ReferenceEquals(n.ModifiedValue, null) || !keep.Contains(n.ModifiedValue));
+ }
+ else
+ {
+ if (data.PrefabModifications != null)
+ {
+ data.PrefabModifications.Clear();
+ }
+
+ if (data.PrefabModificationsReferencedUnityObjects != null)
+ {
+ data.PrefabModificationsReferencedUnityObjects.Clear();
+ }
+ }
+
+ newModifications = newModifications ?? new List<PrefabModification>();
+ PrefabModificationCache.CachePrefabModifications(unityObject, newModifications);
+
+ RegisterPrefabModificationsChange(unityObject, newModifications);
+ }
+ }
+ }
+#endif
+
+ if (!prefabData.ContainsData)
+ {
+ // Sometimes, the prefab hasn't actually been deserialized yet, because
+ // Unity doesn't do anything in a sensible way at all.
+ //
+ // In this case, we have to deserialize from our own data, and just
+ // pretend it's the prefab's data. We can just hope Unity hasn't messed
+ // with the serialized data; it *should* be the same on this instance as
+ // it is on the prefab itself.
+ //
+ // This case occurs often during editor recompile reloads.
+
+ DeserializeUnityObject(unityObject, ref data, context, isPrefabData: true, prefabInstanceUnityObjects: data.ReferencedUnityObjects);
+ }
+ else
+ {
+ // Deserialize the current object with the prefab's data
+ DeserializeUnityObject(unityObject, ref prefabData, context, isPrefabData: true, prefabInstanceUnityObjects: data.ReferencedUnityObjects);
+ }
+
+ // Then apply the prefab modifications using the deserialization context
+ ApplyPrefabModifications(unityObject, data.PrefabModifications, data.PrefabModificationsReferencedUnityObjects);
+
+ return; // Buh bye
+ }
+ }
+ // A straight UnityEngine.Object instance means that the type has been lost to Unity due to a deleted or renamed script
+ // We shouldn't complain in this case, as Unity itself will make it clear to the user that there is something wrong.
+ else if (data.Prefab.GetType() != typeof(UnityEngine.Object))
+ {
+ Debug.LogWarning("The type " + data.Prefab.GetType().GetNiceName() + " no longer supports special prefab serialization (the interface " + typeof(ISupportsPrefabSerialization).GetNiceName() + ") upon deserialization of an instance of a prefab; prefab data may be lost. Has a type been lost?");
+ }
+ }
+
+ var unityObjects = isPrefabData ? prefabInstanceUnityObjects : data.ReferencedUnityObjects;
+
+ if (data.SerializedFormat == DataFormat.Nodes)
+ {
+ // Special case for node format
+ using (var reader = new SerializationNodeDataReader(context))
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ resolver.Value.SetReferencedUnityObjects(unityObjects);
+ context.IndexReferenceResolver = resolver.Value;
+
+ reader.Nodes = data.SerializationNodes;
+
+ UnitySerializationUtility.DeserializeUnityObject(unityObject, reader);
+ }
+ }
+ else if (data.SerializedBytes != null && data.SerializedBytes.Length > 0)
+ {
+ UnitySerializationUtility.DeserializeUnityObject(unityObject, ref data.SerializedBytes, ref unityObjects, data.SerializedFormat, context);
+ }
+ else
+ {
+ UnitySerializationUtility.DeserializeUnityObject(unityObject, ref data.SerializedBytesString, ref unityObjects, data.SerializedFormat, context);
+ }
+
+ // We may have a prefab that has had changes applied to it; either way, apply the stored modifications.
+ ApplyPrefabModifications(unityObject, data.PrefabModifications, data.PrefabModificationsReferencedUnityObjects);
+ }
+ finally
+ {
+ if (cachedContext != null)
+ {
+ Cache<DeserializationContext>.Release(cachedContext);
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void DeserializeUnityObject(UnityEngine.Object unityObject, ref string base64Bytes, ref List<UnityEngine.Object> referencedUnityObjects, DataFormat format, DeserializationContext context = null)
+ {
+ if (string.IsNullOrEmpty(base64Bytes))
+ {
+ return;
+ }
+
+ byte[] bytes = null;
+
+ try
+ {
+ bytes = Convert.FromBase64String(base64Bytes);
+ }
+ catch (FormatException)
+ {
+ Debug.LogError("Invalid base64 string when deserializing data: " + base64Bytes);
+ }
+
+ if (bytes != null)
+ {
+ DeserializeUnityObject(unityObject, ref bytes, ref referencedUnityObjects, format, context);
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void DeserializeUnityObject(UnityEngine.Object unityObject, ref byte[] bytes, ref List<UnityEngine.Object> referencedUnityObjects, DataFormat format, DeserializationContext context = null)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (bytes == null || bytes.Length == 0)
+ {
+ return;
+ }
+
+ if (format == DataFormat.Nodes)
+ {
+ try
+ {
+ Debug.LogError("The serialization data format '" + format.ToString() + "' is not supported by this method. You must create your own reader.");
+ }
+ catch { }
+ return;
+ }
+
+ if (referencedUnityObjects == null)
+ {
+ referencedUnityObjects = new List<UnityEngine.Object>();
+ }
+
+ using (var stream = Cache<CachedMemoryStream>.Claim())
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ stream.Value.MemoryStream.Write(bytes, 0, bytes.Length);
+ stream.Value.MemoryStream.Position = 0;
+
+ resolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
+
+ if (context != null)
+ {
+ context.IndexReferenceResolver = resolver.Value;
+
+ using (var readerCache = GetCachedUnityReader(format, stream.Value.MemoryStream, context))
+ {
+ DeserializeUnityObject(unityObject, readerCache.Value as IDataReader);
+ }
+ }
+ else
+ {
+ using (var con = Cache<DeserializationContext>.Claim())
+ {
+ con.Value.Config.SerializationPolicy = SerializationPolicies.Unity;
+
+ /* If the config instance is not loaded (it should usually be, but in rare cases
+ * it's not), we must not ask for it, as we are not allowed to load from resources
+ * or the asset database during some serialization callbacks.
+ *
+ * (Trying to do that causes internal Unity errors and potentially even crashes.)
+ *
+ * If it's not loaded, we fall back to default values, since there's no other choice.
+ */
+ if (GlobalSerializationConfig.HasInstanceLoaded)
+ {
+ //Debug.Log("Deserializing " + unityObject.GetType().Name + " WITH loaded!");
+ con.Value.Config.DebugContext.ErrorHandlingPolicy = GlobalSerializationConfig.Instance.ErrorHandlingPolicy;
+ con.Value.Config.DebugContext.LoggingPolicy = GlobalSerializationConfig.Instance.LoggingPolicy;
+ con.Value.Config.DebugContext.Logger = GlobalSerializationConfig.Instance.Logger;
+ }
+ else
+ {
+ //Debug.Log("Deserializing " + unityObject.GetType().Name + " WITHOUT loaded!");
+ con.Value.Config.DebugContext.ErrorHandlingPolicy = ErrorHandlingPolicy.Resilient;
+ con.Value.Config.DebugContext.LoggingPolicy = LoggingPolicy.LogErrors;
+ con.Value.Config.DebugContext.Logger = DefaultLoggers.UnityLogger;
+ }
+
+ con.Value.IndexReferenceResolver = resolver.Value;
+
+ using (var readerCache = GetCachedUnityReader(format, stream.Value.MemoryStream, con))
+ {
+ DeserializeUnityObject(unityObject, readerCache.Value as IDataReader);
+ }
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void DeserializeUnityObject(UnityEngine.Object unityObject, IDataReader reader)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (reader == null)
+ {
+ throw new ArgumentNullException("reader");
+ }
+
+ var policyOverride = unityObject as IOverridesSerializationPolicy;
+
+ if (policyOverride != null)
+ {
+ var policy = policyOverride.SerializationPolicy;
+
+ if (policy != null)
+ {
+ reader.Context.Config.SerializationPolicy = policy;
+ }
+ }
+
+ try
+ {
+ reader.PrepareNewSerializationSession();
+
+ var members = FormatterUtilities.GetSerializableMembersMap(unityObject.GetType(), reader.Context.Config.SerializationPolicy);
+
+ int count = 0;
+ string name;
+ EntryType entryType;
+ object unityObjectInstance = unityObject;
+
+ while ((entryType = reader.PeekEntry(out name)) != EntryType.EndOfNode && entryType != EntryType.EndOfArray && entryType != EntryType.EndOfStream)
+ {
+ MemberInfo member = null;
+ WeakValueSetter setter = null;
+
+ bool skip = false;
+
+ if (entryType == EntryType.Invalid)
+ {
+ // Oh boy. We have a lot of logging to do!
+
+ var message = "Encountered invalid entry while reading serialization data for Unity object of type '" + unityObject.GetType().GetNiceFullName() + "'. " +
+ "This likely means that Unity has filled Odin's stored serialization data with garbage, which can randomly happen after upgrading the Unity version of the project, or when otherwise doing things that have a lot of fragile interactions with the asset database. " +
+ "Locating the asset which causes this error log and causing it to reserialize (IE, modifying it and then causing it to be saved to disk) is likely to 'fix' the issue and make this message go away. " +
+ "Even so, DATA MAY HAVE BEEN LOST, and you should verify with your version control system (you're using one, right?!) that everything is alright, and if not, use it to rollback the asset to recover your data.\n\n\n";
+
+#if UNITY_EDITOR
+ // Schedule a delayed log:
+ try
+ {
+ message += "A delayed warning message containing the originating object's name, type and scene/asset path (if applicable) will be scheduled for logging on Unity's main thread. Search for \"DELAYED SERIALIZATION LOG\". " +
+ "This logging callback will also mark the object dirty if it is an asset, hopefully making the issue 'fix' itself. HOWEVER, THERE MAY STILL BE DATA LOSS.\n\n\n";
+
+ EditorApplication_delayCall_Alias += () =>
+ {
+ var log = "DELAYED SERIALIZATION LOG: Name = " + unityObject.name + ", Type = " + unityObject.GetType().GetNiceFullName();
+
+ UnityEngine.Object toPing = unityObject;
+
+ var component = unityObject as Component;
+
+ if (component != null && component.gameObject.scene.IsValid())
+ {
+ log += ", ScenePath = " + component.gameObject.scene.path;
+ }
+
+ if (UnityEditor.AssetDatabase.Contains(unityObject))
+ {
+ var path = UnityEditor.AssetDatabase.GetAssetPath(unityObject);
+ log += ", AssetPath = " + path;
+
+ toPing = UnityEditor.AssetDatabase.LoadMainAssetAtPath(path);
+
+ if (toPing == null) toPing = unityObject;
+
+ UnityEditor.EditorUtility.SetDirty(unityObject);
+ UnityEditor.AssetDatabase.SaveAssets();
+ }
+
+ Debug.LogWarning(log, toPing);
+ };
+ }
+ catch
+ {
+ Debug.LogWarning("DELAYED SERIALIZATION LOG: Delaying log to main thread failed, likely due to a race condition when subscribing to EditorApplication.delayCall; this cannot be guarded against from our code. Try to provoke the error again and hope to get luckier next time!");
+ }
+#endif
+
+ message +=
+ "IF YOU HAVE CONSISTENT REPRODUCTION STEPS THAT MAKE THIS ISSUE REOCCUR, please report it at this issue at 'https://bitbucket.org/sirenix/odin-inspector/issues/526', and copy paste this debug message into your comment, along with any potential actions or recent changes in the project that might have happened to cause this message to occur. " +
+ "If the data dump in this message is cut off, please find the editor's log file (see https://docs.unity3d.com/Manual/LogFiles.html) and copy paste the full version of this message from there.\n\n\n" +
+ "Data dump:\n\n" +
+ " Reader type: " + reader.GetType().Name + "\n";
+
+ try
+ {
+ message += " Data dump: " + reader.GetDataDump();
+ //if (reader is SerializationNodeDataReader)
+ //{
+ // var nodes = (reader as SerializationNodeDataReader).Nodes;
+ // message += " Nodes dump: \n\n" + string.Join("\n", nodes.Select(node => " - Name: " + node.Name + "\n Entry: " + node.Entry + "\n Data: " + node.Data).ToArray());
+ //}
+ //else if (reader.Stream is MemoryStream)
+ //{
+ // message += " Data stream dump (base64): " + ProperBitConverter.BytesToHexString((reader.Stream as MemoryStream).ToArray());
+ //}
+ }
+ finally
+ {
+ reader.Context.Config.DebugContext.LogError(message);
+ skip = true;
+ }
+ }
+ else if (string.IsNullOrEmpty(name))
+ {
+ reader.Context.Config.DebugContext.LogError("Entry of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" is missing a name.");
+ skip = true;
+ }
+ else if (members.TryGetValue(name, out member) == false || (setter = GetCachedUnityMemberSetter(member)) == null)
+ {
+ skip = true;
+ }
+
+ if (skip)
+ {
+ reader.SkipEntry();
+ continue;
+ }
+
+ {
+ Type expectedType = FormatterUtilities.GetContainedType(member);
+ Serializer serializer = Serializer.Get(expectedType);
+
+ try
+ {
+ object value = serializer.ReadValueWeak(reader);
+ setter(ref unityObjectInstance, value);
+ }
+ catch (Exception ex)
+ {
+ reader.Context.Config.DebugContext.LogException(ex);
+ }
+ }
+
+ count++;
+
+ if (count > 1000)
+ {
+ reader.Context.Config.DebugContext.LogError("Breaking out of infinite reading loop! (Read more than a thousand entries for one type!)");
+ break;
+ }
+ }
+ }
+ catch (SerializationAbortException ex)
+ {
+ throw new SerializationAbortException("Deserialization of type '" + unityObject.GetType().GetNiceFullName() + "' aborted.", ex);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(new Exception("Exception thrown while deserializing type '" + unityObject.GetType().GetNiceFullName() + "': " + ex.Message, ex));
+ }
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static List<string> SerializePrefabModifications(List<PrefabModification> modifications, ref List<UnityEngine.Object> referencedUnityObjects)
+ {
+ if (referencedUnityObjects == null)
+ {
+ referencedUnityObjects = new List<UnityEngine.Object>();
+ }
+ else if (referencedUnityObjects.Count > 0)
+ {
+ referencedUnityObjects.Clear();
+ }
+
+ if (modifications == null || modifications.Count == 0)
+ {
+ return new List<string>();
+ }
+
+ // Sort modifications alphabetically by path; this will ensure that modifications
+ // to child paths are always applied after modifications to the parent paths
+ modifications.Sort((a, b) =>
+ {
+ int compared = a.Path.CompareTo(b.Path);
+
+ if (compared == 0)
+ {
+ if ((a.ModificationType == PrefabModificationType.ListLength || a.ModificationType == PrefabModificationType.Dictionary) && b.ModificationType == PrefabModificationType.Value)
+ {
+ return 1;
+ }
+ else if (a.ModificationType == PrefabModificationType.Value && (b.ModificationType == PrefabModificationType.ListLength || b.ModificationType == PrefabModificationType.Dictionary))
+ {
+ return -1;
+ }
+ }
+
+ return compared;
+ });
+
+ List<string> result = new List<string>();
+
+ using (var context = Cache<SerializationContext>.Claim())
+ using (var stream = CachedMemoryStream.Claim())
+ using (var writerCache = Cache<JsonDataWriter>.Claim())
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ var writer = writerCache.Value;
+
+ writer.Context = context;
+ writer.Stream = stream.Value.MemoryStream;
+ writer.PrepareNewSerializationSession();
+ writer.FormatAsReadable = false;
+ writer.EnableTypeOptimization = false;
+
+ resolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
+ writer.Context.IndexReferenceResolver = resolver.Value;
+
+ for (int i = 0; i < modifications.Count; i++)
+ {
+ var mod = modifications[i];
+
+ if (mod.ModificationType == PrefabModificationType.ListLength)
+ {
+ writer.MarkJustStarted();
+ writer.WriteString("path", mod.Path);
+ writer.WriteInt32("length", mod.NewLength);
+
+ writer.FlushToStream();
+ result.Add(GetStringFromStreamAndReset(stream.Value.MemoryStream));
+ }
+ else if (mod.ModificationType == PrefabModificationType.Value)
+ {
+ writer.MarkJustStarted();
+ writer.WriteString("path", mod.Path);
+
+ if (mod.ReferencePaths != null && mod.ReferencePaths.Count > 0)
+ {
+ writer.BeginStructNode("references", null);
+ {
+ for (int j = 0; j < mod.ReferencePaths.Count; j++)
+ {
+ writer.WriteString(null, mod.ReferencePaths[j]);
+ }
+ }
+ writer.EndNode("references");
+ }
+
+ var serializer = Serializer.Get<object>();
+ serializer.WriteValueWeak("value", mod.ModifiedValue, writer);
+
+ writer.FlushToStream();
+ result.Add(GetStringFromStreamAndReset(stream.Value.MemoryStream));
+ }
+ else if (mod.ModificationType == PrefabModificationType.Dictionary)
+ {
+ writer.MarkJustStarted();
+ writer.WriteString("path", mod.Path);
+
+ Serializer.Get<object[]>().WriteValue("add_keys", mod.DictionaryKeysAdded, writer);
+ Serializer.Get<object[]>().WriteValue("remove_keys", mod.DictionaryKeysRemoved, writer);
+
+ writer.FlushToStream();
+ result.Add(GetStringFromStreamAndReset(stream.Value.MemoryStream));
+ }
+
+ // We don't want modifications to be able to reference each other
+ writer.Context.ResetInternalReferences();
+ }
+ }
+
+ return result;
+ }
+
+ private static string GetStringFromStreamAndReset(Stream stream)
+ {
+ byte[] bytes = new byte[stream.Position];
+ stream.Position = 0;
+ stream.Read(bytes, 0, bytes.Length);
+ stream.Position = 0;
+
+ return Encoding.UTF8.GetString(bytes);
+ }
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static List<PrefabModification> DeserializePrefabModifications(List<string> modifications, List<UnityEngine.Object> referencedUnityObjects)
+ {
+ if (modifications == null || modifications.Count == 0)
+ {
+ // Nothing to apply
+ return new List<PrefabModification>();
+ }
+
+ List<PrefabModification> result = new List<PrefabModification>();
+
+ int longestByteCount = 0;
+
+ for (int i = 0; i < modifications.Count; i++)
+ {
+ int count = modifications[i].Length * 2;
+
+ if (count > longestByteCount)
+ {
+ longestByteCount = count;
+ }
+ }
+
+ using (var context = Cache<DeserializationContext>.Claim())
+ using (var streamCache = CachedMemoryStream.Claim(longestByteCount))
+ using (var readerCache = Cache<JsonDataReader>.Claim())// GetCachedUnityReader(DataFormat.JSON, streamCache.Value.MemoryStream, context))
+ using (var resolver = Cache<UnityReferenceResolver>.Claim())
+ {
+ var stream = streamCache.Value.MemoryStream;
+ var reader = readerCache.Value;
+
+ reader.Context = context;
+ reader.Stream = stream;
+
+ resolver.Value.SetReferencedUnityObjects(referencedUnityObjects);
+ reader.Context.IndexReferenceResolver = resolver.Value;
+
+ for (int i = 0; i < modifications.Count; i++)
+ {
+ string modStr = modifications[i];
+ byte[] bytes = Encoding.UTF8.GetBytes(modStr);
+
+ stream.SetLength(bytes.Length);
+ stream.Position = 0;
+ stream.Write(bytes, 0, bytes.Length);
+ stream.Position = 0;
+
+ PrefabModification modification = new PrefabModification();
+
+ string entryName;
+ EntryType entryType;
+
+ reader.PrepareNewSerializationSession();
+
+ entryType = reader.PeekEntry(out entryName);
+
+ if (entryType == EntryType.EndOfStream)
+ {
+ // We might have reached the end of stream from a prior modification string
+ // If we have, force our way into the first entry in the new string
+ reader.SkipEntry();
+ }
+
+ while ((entryType = reader.PeekEntry(out entryName)) != EntryType.EndOfNode && entryType != EntryType.EndOfArray && entryType != EntryType.EndOfStream)
+ {
+ if (entryName == null)
+ {
+ Debug.LogError("Unexpected entry of type " + entryType + " without a name.");
+ reader.SkipEntry();
+ continue;
+ }
+
+ if (entryName.Equals("path", StringComparison.InvariantCultureIgnoreCase))
+ {
+ reader.ReadString(out modification.Path);
+ }
+ else if (entryName.Equals("length", StringComparison.InvariantCultureIgnoreCase))
+ {
+ reader.ReadInt32(out modification.NewLength);
+ modification.ModificationType = PrefabModificationType.ListLength;
+ }
+ else if (entryName.Equals("references", StringComparison.InvariantCultureIgnoreCase))
+ {
+ modification.ReferencePaths = new List<string>();
+
+ Type dummy;
+ reader.EnterNode(out dummy);
+ {
+ while (reader.PeekEntry(out entryName) == EntryType.String)
+ {
+ string path;
+ reader.ReadString(out path);
+ modification.ReferencePaths.Add(path);
+ }
+ }
+ reader.ExitNode();
+ }
+ else if (entryName.Equals("value", StringComparison.InvariantCultureIgnoreCase))
+ {
+ modification.ModifiedValue = Serializer.Get<object>().ReadValue(reader);
+ modification.ModificationType = PrefabModificationType.Value;
+ }
+ else if (entryName.Equals("add_keys", StringComparison.InvariantCultureIgnoreCase))
+ {
+ modification.DictionaryKeysAdded = Serializer.Get<object[]>().ReadValue(reader);
+ modification.ModificationType = PrefabModificationType.Dictionary;
+ }
+ else if (entryName.Equals("remove_keys", StringComparison.InvariantCultureIgnoreCase))
+ {
+ modification.DictionaryKeysRemoved = Serializer.Get<object[]>().ReadValue(reader);
+ modification.ModificationType = PrefabModificationType.Dictionary;
+ }
+ else
+ {
+ Debug.LogError("Unexpected entry name '" + entryName + "' while deserializing prefab modifications.");
+ reader.SkipEntry();
+ }
+ }
+
+ if (modification.Path == null)
+ {
+ // This happens quite often if you change the structure of a class which cointains prefab modifications made to it.
+ // Those invalid prefab modifications should be removed, which they seem to be in most cases, but apparently not some.
+ //
+ // And debugging a warning here makes give errors because of the bug in VS Bridge. And prevents people from bulding.
+ // Debug.LogWarning("Error when deserializing prefab modification; no path found. Modification lost; string was: '" + modStr + "'.");
+ continue;
+ }
+
+ result.Add(modification);
+ }
+ }
+
+ return result;
+ }
+
+#if UNITY_EDITOR
+
+ /// <summary>
+ /// Not yet documented.
+ /// </summary>
+ public static void RegisterPrefabModificationsChange(UnityEngine.Object unityObject, List<PrefabModification> modifications)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+#if PREFAB_DEBUG
+ //Debug.Log((Event.current == null ? "NO EVENT" : Event.current.type.ToString()) + ": Registering " + (modifications == null ? 0 : modifications.Count) + " modifications to " + unityObject.name + ":");
+ Debug.Log("Registering " + (modifications == null ? 0 : modifications.Count) + " prefab modifications: ");
+
+ for (int i = 0; i < modifications.Count; i++)
+ {
+ var mod = modifications[i];
+
+ if (mod.ModificationType == PrefabModificationType.ListLength)
+ {
+ Debug.Log(" LENGTH@" + mod.Path + ": " + mod.NewLength);
+ }
+ else if (mod.ModificationType == PrefabModificationType.Dictionary)
+ {
+ Debug.Log(" DICT@" + mod.Path + ": (add: " + (mod.DictionaryKeysAdded != null ? mod.DictionaryKeysAdded.Length : 0) + ") -- (remove: " + (mod.DictionaryKeysRemoved != null ? mod.DictionaryKeysRemoved.Length : 0) + ")");
+ }
+ else
+ {
+ string str;
+
+ if (mod.ModifiedValue == null)
+ {
+ str = "null";
+ }
+ else if (typeof(UnityEngine.Object).IsAssignableFrom(mod.ModificationType.GetType()))
+ {
+ str = "Unity object";
+ }
+ else
+ {
+ str = mod.ModificationType.ToString();
+ }
+
+ Debug.Log(" VALUE@" + mod.Path + ": " + str);
+ }
+ }
+#endif
+
+ PrefabModificationCache.CachePrefabModifications(unityObject, modifications);
+ RegisteredPrefabModifications[unityObject] = modifications;
+ }
+
+#endif
+
+ /// <summary>
+ /// Creates an object with default values initialized in the style of Unity; strings will be "", classes will be instantiated recursively with default values, and so on.
+ /// </summary>
+ public static object CreateDefaultUnityInitializedObject(Type type)
+ {
+ Assert.IsNotNull(type);
+ Assert.IsFalse(type.IsAbstract);
+ Assert.IsFalse(type.IsInterface);
+
+ return CreateDefaultUnityInitializedObject(type, 0);
+ }
+
+ private static object CreateDefaultUnityInitializedObject(Type type, int depth)
+ {
+ if (depth > 5)
+ {
+ return null;
+ }
+
+ if (!UnitySerializationUtility.GuessIfUnityWillSerialize(type))
+ {
+ return type.IsValueType ? Activator.CreateInstance(type) : null;
+ }
+
+ if (type == typeof(string))
+ {
+ return "";
+ }
+ else if (type.IsEnum)
+ {
+ var values = Enum.GetValues(type);
+ return values.Length > 0 ? values.GetValue(0) : Enum.ToObject(type, 0);
+ }
+ else if (type.IsPrimitive)
+ {
+ return Activator.CreateInstance(type);
+ }
+ else if (type.IsArray)
+ {
+ Assert.IsTrue(type.GetArrayRank() == 1);
+ return Array.CreateInstance(type.GetElementType(), 0);
+ }
+ else if (type.ImplementsOpenGenericClass(typeof(List<>)) || typeof(UnityEventBase).IsAssignableFrom(type))
+ {
+ try
+ {
+ return Activator.CreateInstance(type);
+ }
+ catch
+ {
+ return null;
+ }
+ }
+ else if (typeof(UnityEngine.Object).IsAssignableFrom(type))
+ {
+ return null;
+ }
+ else if ((type.Assembly.GetName().Name.StartsWith("UnityEngine") || type.Assembly.GetName().Name.StartsWith("UnityEditor")) && type.GetConstructor(Type.EmptyTypes) != null)
+ {
+ try
+ {
+ return Activator.CreateInstance(type);
+ }
+ catch (Exception ex)
+ {
+ Debug.LogException(ex);
+ return null;
+ }
+ }
+
+ object value;
+
+ if (type.GetConstructor(Type.EmptyTypes) != null)
+ {
+ return Activator.CreateInstance(type);
+ }
+
+ value = FormatterServices.GetUninitializedObject(type);
+
+ var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+
+ for (int i = 0; i < fields.Length; i++)
+ {
+ var field = fields[i];
+ if (GuessIfUnityWillSerialize(field))
+ {
+ field.SetValue(value, CreateDefaultUnityInitializedObject(field.FieldType, depth + 1));
+ }
+ }
+
+ return value;
+ }
+
+ private static void ApplyPrefabModifications(UnityEngine.Object unityObject, List<string> modificationData, List<UnityEngine.Object> referencedUnityObjects)
+ {
+ if (unityObject == null)
+ {
+ throw new ArgumentNullException("unityObject");
+ }
+
+ if (modificationData == null || modificationData.Count == 0)
+ {
+#if UNITY_EDITOR
+ PrefabModificationCache.CachePrefabModifications(unityObject, new List<PrefabModification>());
+#endif
+
+ // Nothing to apply.
+ return;
+ }
+
+ var modifications = DeserializePrefabModifications(modificationData, referencedUnityObjects);
+
+#if UNITY_EDITOR
+ PrefabModificationCache.CachePrefabModifications(unityObject, modifications);
+#endif
+
+ for (int i = 0; i < modifications.Count; i++)
+ {
+ var mod = modifications[i];
+
+ try
+ {
+ mod.Apply(unityObject);
+ }
+ catch (Exception ex)
+ {
+ Debug.Log("The following exception was thrown when trying to apply a prefab modification for path '" + mod.Path + "':");
+ Debug.LogException(ex);
+ }
+ }
+ }
+
+ private static WeakValueGetter GetCachedUnityMemberGetter(MemberInfo member)
+ {
+ lock (UnityMemberGetters)
+ {
+ WeakValueGetter result;
+
+ if (UnityMemberGetters.TryGetValue(member, out result) == false)
+ {
+ if (member is FieldInfo)
+ {
+ result = EmitUtilities.CreateWeakInstanceFieldGetter(member.DeclaringType, member as FieldInfo);
+ }
+ else if (member is PropertyInfo)
+ {
+ result = EmitUtilities.CreateWeakInstancePropertyGetter(member.DeclaringType, member as PropertyInfo);
+ }
+ else
+ {
+ result = delegate (ref object instance)
+ {
+ return FormatterUtilities.GetMemberValue(member, instance);
+ };
+ }
+
+ UnityMemberGetters.Add(member, result);
+ }
+
+ return result;
+ }
+ }
+
+ private static WeakValueSetter GetCachedUnityMemberSetter(MemberInfo member)
+ {
+ lock (UnityMemberSetters)
+ {
+ WeakValueSetter result;
+
+ if (UnityMemberSetters.TryGetValue(member, out result) == false)
+ {
+ if (member is FieldInfo)
+ {
+ result = EmitUtilities.CreateWeakInstanceFieldSetter(member.DeclaringType, member as FieldInfo);
+ }
+ else if (member is PropertyInfo)
+ {
+ result = EmitUtilities.CreateWeakInstancePropertySetter(member.DeclaringType, member as PropertyInfo);
+ }
+ else
+ {
+ result = delegate (ref object instance, object value)
+ {
+ FormatterUtilities.SetMemberValue(member, instance, value);
+ };
+ }
+
+ UnityMemberSetters.Add(member, result);
+ }
+
+ return result;
+ }
+ }
+
+ private static ICache GetCachedUnityWriter(DataFormat format, Stream stream, SerializationContext context)
+ {
+ ICache cache;
+
+ switch (format)
+ {
+ case DataFormat.Binary:
+ {
+ var c = Cache<BinaryDataWriter>.Claim();
+ c.Value.Stream = stream;
+ cache = c;
+ }
+ break;
+ case DataFormat.JSON:
+ {
+ var c = Cache<JsonDataWriter>.Claim();
+ c.Value.Stream = stream;
+ cache = c;
+ }
+ break;
+ case DataFormat.Nodes:
+ throw new InvalidOperationException("Don't do this for nodes!");
+ default:
+ throw new NotImplementedException(format.ToString());
+ }
+
+ (cache.Value as IDataWriter).Context = context;
+
+ return cache;
+
+ //IDataWriter writer;
+
+ //if (UnityWriters.TryGetValue(format, out writer) == false)
+ //{
+ // writer = SerializationUtility.CreateWriter(stream, context, format);
+ // UnityWriters.Add(format, writer);
+ //}
+ //else
+ //{
+ // writer.Context = context;
+
+ // if (writer is BinaryDataWriter)
+ // {
+ // (writer as BinaryDataWriter).Stream = stream;
+ // }
+ // else if (writer is JsonDataWriter)
+ // {
+ // (writer as JsonDataWriter).Stream = stream;
+ // }
+ //}
+
+ //return writer;
+ }
+
+ private static ICache GetCachedUnityReader(DataFormat format, Stream stream, DeserializationContext context)
+ {
+ ICache cache;
+
+ switch (format)
+ {
+ case DataFormat.Binary:
+ {
+ var c = Cache<BinaryDataReader>.Claim();
+ c.Value.Stream = stream;
+ cache = c;
+ }
+ break;
+ case DataFormat.JSON:
+ {
+ var c = Cache<JsonDataReader>.Claim();
+ c.Value.Stream = stream;
+ cache = c;
+ }
+ break;
+ case DataFormat.Nodes:
+ throw new InvalidOperationException("Don't do this for nodes!");
+ default:
+ throw new NotImplementedException(format.ToString());
+ }
+
+ (cache.Value as IDataReader).Context = context;
+
+ return cache;
+
+ //if (UnityReaders.TryGetValue(format, out reader) == false)
+ //{
+ // reader = SerializationUtility.CreateReader(stream, context, format);
+ // UnityReaders.Add(format, reader);
+ //}
+ //else
+ //{
+ // reader.Context = context;
+
+ // if (reader is BinaryDataReader)
+ // {
+ // (reader as BinaryDataReader).Stream = stream;
+ // }
+ // else if (reader is JsonDataReader)
+ // {
+ // (reader as JsonDataReader).Stream = stream;
+ // }
+ //}
+
+ //return reader;
+ }
+
+#if UNITY_EDITOR
+
+ [UnityEditor.InitializeOnLoad]
+ private static class PrefabSelectionTracker
+ {
+ private static readonly object LOCK = new object();
+ private static readonly HashSet<UnityEngine.Object> selectedPrefabObjects;
+
+ static PrefabSelectionTracker()
+ {
+ selectedPrefabObjects = new HashSet<UnityEngine.Object>(ReferenceEqualityComparer<UnityEngine.Object>.Default);
+ UnityEditor.Selection.selectionChanged += OnSelectionChanged;
+ OnSelectionChanged();
+ }
+
+ public static bool IsCurrentlySelectedPrefabRoot(UnityEngine.Object obj)
+ {
+ lock (LOCK)
+ {
+ return selectedPrefabObjects.Contains(obj);
+ }
+
+ //var component = obj as Component;
+
+ //if (object.ReferenceEquals(component, null))
+ //{
+ // Debug.LogError("A non-component type Unity object (type '" + obj.GetType() + "') is acting like a prefab. What?", obj);
+ // return false;
+ //}
+
+ //var prefabRoot = UnityEditor.PrefabUtility.FindPrefabRoot(component.gameObject);
+
+ //return prefabRoot != null && PrefabSelectionTracker.SelectedPrefabRoots.Contains(prefabRoot);
+
+ //var selectedObjects = PrefabSelectionTracker.SelectedPrefabRoots;
+
+ //for (int i = 0; i < selectedObjects.Count; i++)
+ //{
+ // if (object.ReferenceEquals(obj, selectedObjects[i]))
+ // {
+ // return true;
+ // }
+ //}
+
+ //return false;
+ }
+
+ private static void OnSelectionChanged()
+ {
+ lock (LOCK)
+ {
+ selectedPrefabObjects.Clear();
+
+ var rootPrefabs = UnityEditor.Selection.objects
+ .Where(n =>
+ {
+ if (!(n is GameObject)) return false;
+
+ var prefabType = UnityEditor.PrefabUtility.GetPrefabType(n);
+ return prefabType == UnityEditor.PrefabType.Prefab
+ || prefabType == UnityEditor.PrefabType.ModelPrefab
+ || prefabType == UnityEditor.PrefabType.PrefabInstance
+ || prefabType == UnityEditor.PrefabType.ModelPrefabInstance;
+ })
+ .Select(n => UnityEditor.PrefabUtility.FindPrefabRoot((GameObject)n))
+ .Distinct();
+
+ foreach (var root in rootPrefabs)
+ {
+ RegisterRecursive(root);
+ }
+ }
+ //SelectedPrefabRoots.AddRange(selection);
+
+ //for (int i = 0; i < selection.Length; i++)
+ //{
+ // var obj = selection[i];
+
+ // GameObject gameObject = obj as GameObject;
+
+ // if (!gameObject.SafeIsUnityNull())
+ // {
+ // SelectedPrefabRoots.AddRange(gameObject.GetComponents(typeof(Component)));
+ // }
+ //}
+ }
+
+ private static void RegisterRecursive(GameObject go)
+ {
+ selectedPrefabObjects.Add(go);
+
+ var components = go.GetComponents<Component>();
+
+ for (int i = 0; i < components.Length; i++)
+ {
+ selectedPrefabObjects.Add(components[i]);
+ }
+
+ var transform = go.transform;
+
+ for (int i = 0; i < transform.childCount; i++)
+ {
+ var child = transform.GetChild(i);
+ RegisterRecursive(child.gameObject);
+ }
+ }
+ }
+
+
+ public static class PrefabModificationCache
+ {
+ private static readonly Dictionary<object, List<PrefabModification>> CachedDeserializedModifications = new Dictionary<object, List<PrefabModification>>(ReferenceEqualityComparer<object>.Default);
+ private static readonly Dictionary<object, int> CachedDeserializedModificationTimes = new Dictionary<object, int>(ReferenceEqualityComparer<object>.Default);
+
+ private static readonly object Caches_LOCK = new object();
+
+ private static int counter = 0;
+
+ public static List<PrefabModification> DeserializePrefabModificationsCached(UnityEngine.Object obj, List<string> modifications, List<UnityEngine.Object> referencedUnityObjects)
+ {
+ lock (Caches_LOCK)
+ {
+ List<PrefabModification> result;
+
+ if (!CachedDeserializedModifications.TryGetValue(obj, out result))
+ {
+ result = DeserializePrefabModifications(modifications, referencedUnityObjects);
+ CachedDeserializedModifications.Add(obj, result);
+ }
+
+ CachedDeserializedModificationTimes[obj] = ++counter;
+ PrunePrefabModificationsCache();
+
+ return result;
+ }
+ }
+
+ public static void CachePrefabModifications(UnityEngine.Object obj, List<PrefabModification> modifications)
+ {
+ lock (Caches_LOCK)
+ {
+ CachedDeserializedModifications[obj] = modifications;
+ CachedDeserializedModificationTimes[obj] = ++counter;
+ PrunePrefabModificationsCache();
+ }
+ }
+
+ private static void PrunePrefabModificationsCache()
+ {
+ const int CACHE_SIZE = 10;
+
+ if (CachedDeserializedModifications.Count != CachedDeserializedModificationTimes.Count)
+ {
+ CachedDeserializedModifications.Clear();
+ CachedDeserializedModificationTimes.Clear();
+ }
+
+ // Once, this was a 'while count > CACHE_SIZE' loop, but in certain cases that can infinite loop
+ // so now it's a simpler and harder-to-break for loop with extra debugging clauses in the body.
+ int removeCount = CachedDeserializedModificationTimes.Count - CACHE_SIZE;
+
+ for (int i = 0; i < removeCount; i++)
+ {
+ object lowestObj = null;
+ int lowestTime = int.MaxValue;
+
+ foreach (var pair in CachedDeserializedModificationTimes)
+ {
+ if (pair.Value < lowestTime)
+ {
+ lowestObj = pair.Key;
+ lowestTime = pair.Value;
+ }
+ }
+
+ CachedDeserializedModifications.Remove(lowestObj);
+ if (!CachedDeserializedModificationTimes.Remove(lowestObj))
+ {
+ Debug.LogError("A Unity object instance of type '" + lowestObj.GetType().GetNiceName() + "' has likely become corrupt or destroyed somehow, yet deserialization has been invoked for it. If you're in the editor, you can click this log message to attempt to highlight the object. (It probably won't work, but there's a chance. If the highlighting doesn't work, the object instance is so broken that Odin cannot give you any more info about it than this message contains. Good luck!)", lowestObj as UnityEngine.Object);
+
+ // There are bad keys in the dictionaries; we have to clear them and just rebuild the cache.
+ // This theory isn't confirmed, but it's probably because UnityEngine.Object.GetHashCode()
+ // returns inconsistent results/changes for destroyed objects.
+ //
+ // If we don't clear the dictionaries, we will never be able to remove the bad keys. In olden
+ // days, this was the cause of infinite looping.
+
+ CachedDeserializedModifications.Clear();
+ CachedDeserializedModificationTimes.Clear();
+ }
+ }
+ }
+ }
+
+ private static readonly MemberInfo EditorApplication_delayCall_Member = typeof(UnityEditor.EditorApplication).GetMember("delayCall", Flags.StaticAnyVisibility).FirstOrDefault();
+
+ /// <summary>
+ /// In 2020.1, Unity changed EditorApplication.delayCall from a field to an event, meaning
+ /// we now have to use reflection to access it consistently across all versions of Unity.
+ /// </summary>
+ private static event Action EditorApplication_delayCall_Alias
+ {
+ add
+ {
+ if (EditorApplication_delayCall_Member == null) throw new InvalidOperationException("EditorApplication.delayCall field or event could not be found. Odin will be broken.");
+
+ if (EditorApplication_delayCall_Member is FieldInfo)
+ {
+ UnityEditor.EditorApplication.CallbackFunction val = (UnityEditor.EditorApplication.CallbackFunction)(EditorApplication_delayCall_Member as FieldInfo).GetValue(null);
+ val += value.ConvertDelegate<UnityEditor.EditorApplication.CallbackFunction>();
+ (EditorApplication_delayCall_Member as FieldInfo).SetValue(null, val);
+ }
+ else if (EditorApplication_delayCall_Member is EventInfo)
+ {
+ (EditorApplication_delayCall_Member as EventInfo).AddEventHandler(null, value);
+ }
+ else
+ {
+ if (EditorApplication_delayCall_Member == null) throw new InvalidOperationException("EditorApplication.delayCall was not a field or an event. Odin will be broken.");
+ }
+ }
+ remove
+ {
+ if (EditorApplication_delayCall_Member == null) throw new InvalidOperationException("EditorApplication.delayCall field or event could not be found. Odin will be broken.");
+
+ if (EditorApplication_delayCall_Member is FieldInfo)
+ {
+ UnityEditor.EditorApplication.CallbackFunction val = (UnityEditor.EditorApplication.CallbackFunction)(EditorApplication_delayCall_Member as FieldInfo).GetValue(null);
+ val -= value.ConvertDelegate<UnityEditor.EditorApplication.CallbackFunction>();
+ (EditorApplication_delayCall_Member as FieldInfo).SetValue(null, val);
+ }
+ else if (EditorApplication_delayCall_Member is EventInfo)
+ {
+ (EditorApplication_delayCall_Member as EventInfo).RemoveEventHandler(null, value);
+ }
+ else
+ {
+ if (EditorApplication_delayCall_Member == null) throw new InvalidOperationException("EditorApplication.delayCall was not a field or an event. Odin will be broken.");
+ }
+ }
+ }
+
+ private static T ConvertDelegate<T>(this Delegate src)
+ {
+ if (src == null || src.GetType() == typeof(T))
+ return (T)(object)src;
+
+ if (src.GetInvocationList().Count() == 1)
+ {
+ return (T)(object)Delegate.CreateDelegate(typeof(T), src.Target, src.Method);
+ }
+ else
+ {
+ return (T)(object)src.GetInvocationList().Aggregate<Delegate, Delegate>(null, (current, d) => Delegate.Combine(current, (Delegate)(object)ConvertDelegate<T>(d)));
+ }
+ }
+
+#endif
+ }
+} \ No newline at end of file
diff --git a/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta
new file mode 100644
index 00000000..53e93183
--- /dev/null
+++ b/VRCSDK3Worlds/Assets/Udon/Serialization/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9eb15f2339819bb651c7872d73c89776
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant: