diff --git a/DataSerializer/ApplicationStateObserver.cs b/DataSerializer/ApplicationStateObserver.cs index 04d830c..ebe7d71 100644 --- a/DataSerializer/ApplicationStateObserver.cs +++ b/DataSerializer/ApplicationStateObserver.cs @@ -5,11 +5,13 @@ namespace ToolBox.Serialization { internal sealed class ApplicationStateObserver : MonoBehaviour { - public event Action OnQuit = null; + public event Action OnQuit; #if !UNITY_IOS && !UNITY_ANDROID - private void OnApplicationQuit() => + private void OnApplicationQuit() + { OnQuit?.Invoke(); + } #else private void OnApplicationPause(bool pause) { diff --git a/DataSerializer/AssemblyGenerator.cs b/DataSerializer/AssemblyGenerator.cs index 7c52241..b18ad03 100644 --- a/DataSerializer/AssemblyGenerator.cs +++ b/DataSerializer/AssemblyGenerator.cs @@ -30,7 +30,6 @@ public void OnPreprocessBuild(BuildReport report) && AOTSupportUtilities.ScanProjectForSerializedTypes(out var types)) { types.Add(typeof(byte[])); - types.Add(typeof(Item)); var providers = AppDomain .CurrentDomain diff --git a/DataSerializer/AssetsContainer.cs b/DataSerializer/AssetsContainer.cs index 3b066a2..c0ec73f 100644 --- a/DataSerializer/AssetsContainer.cs +++ b/DataSerializer/AssetsContainer.cs @@ -1,137 +1,116 @@ using System.Collections.Generic; using System.Linq; -using ToolBox.Serialization.OdinSerializer; #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; +using Object = UnityEngine.Object; namespace ToolBox.Serialization { - internal sealed class AssetsContainer : ScriptableObject, IExternalStringReferenceResolver - { - [SerializeField] private AssetEntry[] _savedAssets = null; - [SerializeField] private string[] _paths = null; - - public IExternalStringReferenceResolver NextResolver { get; set; } - - public bool CanReference(object value, out string id) - { - id = null; - - if (!(value is Object obj) || !TryGetValue(obj, out var entry)) - return false; - - id = entry.Guid; - return true; - } - - public bool TryResolveReference(string id, out object value) - { - value = null; - - if (id == null) - return false; - - bool contains = TryGetValue(id, out var entry); - value = entry.Asset; - - return contains; - } + public sealed class AssetsContainer : ScriptableObject + { + [SerializeField] private Object[] _savedAssets; + [SerializeField] private string[] _paths; + + public bool TryGetObject(ushort id, out Object entry) + { + entry = null; + + if (id == 0 || id >= _savedAssets.Length) + { + return false; + } + + entry = _savedAssets[id]; + return true; + } + + public bool TryGetId(Object value, out ushort id) + { + id = 0; + + for (ushort i = 1; i < _savedAssets.Length; i++) + { + if (_savedAssets[i] != value) + { + continue; + } + + id = i; + return true; + } + + return false; + } #if UNITY_EDITOR - // TODO: Make everything with loops and lists instead of LINQ - public void LoadAssets() - { - if (_paths == null) - return; - - _paths = _paths.Where(x => !string.IsNullOrEmpty(x) && AssetDatabase.IsValidFolder(x)).ToArray(); - - if (_paths.Length == 0) - return; - - List assets; - - assets = AssetDatabase - .FindAssets("t:Object", _paths) - .Select(AssetDatabase.GUIDToAssetPath) - .Select(AssetDatabase.LoadAssetAtPath) - .Where(x => - { - string fileNamespace = x.GetType().Namespace; - - return x != null && (fileNamespace == null || !fileNamespace.Contains("UnityEditor")); - }) // Change UnityEditor to Editor? - .ToList(); - - var newEntries = new List(); - - foreach (var asset in assets) - { - string path = AssetDatabase.GetAssetPath(asset); - string guid = AssetDatabase.AssetPathToGUID(path); - - if (!TryGetValue(asset, out _)) - newEntries.Add(new AssetEntry(guid, asset)); - - var childAssets = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GetAssetPath(asset)); - - for (int i = 0; i < childAssets.Length; i++) - { - var child = childAssets[i]; - - if (!TryGetValue(child, out _)) - { - string childGuid = System.Guid.NewGuid().ToString(); - newEntries.Add(new AssetEntry(childGuid, child)); - } - } - } - - ArrayUtility.AddRange(ref _savedAssets, newEntries.ToArray()); - EditorUtility.SetDirty(this); - } - - public void Clear() - { - _savedAssets = new AssetEntry[0]; - EditorUtility.SetDirty(this); - } + public void LoadAssets() + { + if (_paths == null) + { + return; + } + + _paths = _paths.Where(x => !string.IsNullOrEmpty(x) && AssetDatabase.IsValidFolder(x)).ToArray(); + + if (_paths.Length == 0) + { + return; + } + + // ReSharper disable once UseArrayEmptyMethod + _savedAssets ??= new Object[0]; + + var assets = AssetDatabase + .FindAssets("t:Object", _paths) + .Select(AssetDatabase.GUIDToAssetPath) + .Select(AssetDatabase.LoadAssetAtPath) + .Where(x => + { + var fileNamespace = x.GetType().Namespace; + + return x != null && (fileNamespace == null || !fileNamespace.Contains("UnityEditor")); + }) + .ToList(); + + var newEntries = new List(); + + foreach (var asset in assets) + { + if (!TryGetId(asset, out _)) + { + newEntries.Add(asset); + } + + var children = AssetDatabase.LoadAllAssetRepresentationsAtPath(AssetDatabase.GetAssetPath(asset)); + + foreach (var child in children) + { + if (TryGetId(child, out _)) + { + continue; + } + + newEntries.Add(child); + } + } + + ArrayUtility.AddRange(ref _savedAssets, newEntries.ToArray()); + + if (_savedAssets.Length == 0 || _savedAssets[0] != null) + { + ArrayUtility.Insert(ref _savedAssets, 0, null); + } + + EditorUtility.SetDirty(this); + } + + public void Clear() + { + _savedAssets = null; + EditorUtility.SetDirty(this); + } #endif - - private bool TryGetValue(string guid, out AssetEntry entry) - { - for (int i = 0; i < _savedAssets.Length; i++) - { - var asset = _savedAssets[i]; - - if (asset.Guid == guid) - { - entry = asset; - return true; - } - } - - entry = null; - return false; - } - - private bool TryGetValue(Object obj, out AssetEntry entry) - { - for (int i = 0; i < _savedAssets.Length; i++) - { - var asset = _savedAssets[i]; - - if (asset.Asset == obj) - { - entry = asset; - return true; - } - } - - entry = null; - return false; - } - } -} + } +} \ No newline at end of file diff --git a/DataSerializer/DataSerializer.cs b/DataSerializer/DataSerializer.cs index 82827cb..008b50e 100644 --- a/DataSerializer/DataSerializer.cs +++ b/DataSerializer/DataSerializer.cs @@ -8,37 +8,29 @@ namespace ToolBox.Serialization { public static class DataSerializer { - private static Dictionary _data = null; - private static int _currentProfileIndex = 0; - private static string _savePath = string.Empty; - private static SerializationContext _serializationContext = null; - private static DeserializationContext _deserializationContext = null; - private static AssetsContainer _container = null; - - private const string FILE_NAME = "Save"; - private const DataFormat DATA_FORMAT = DataFormat.Binary; - private const int INITIAL_SIZE = 64; - - public static event Action FileSaving = null; + private static Dictionary _data; + private static int _currentProfileIndex; + private static string _savePath; + private static SerializationContext _serializationContext; + private static DeserializationContext _deserializationContext; + private static ReferenceResolver _referenceResolver; + private const string FileName = "Save"; + private const DataFormat DataFormat = OdinSerializer.DataFormat.Binary; + private const int InitialSize = 64; + + public static AssetsContainer Container { get; private set; } + public static event Action FileSaving; public static void Save(string key, T dataToSave) { - if (_data.TryGetValue(key, out var data)) - { - data.Value = Serialize(dataToSave); - } - else - { - var saveItem = new Item { Value = Serialize(dataToSave) }; - _data.Add(key, saveItem); - } + _data[key] = Serialize(dataToSave); } public static T Load(string key) { _data.TryGetValue(key, out var value); - return Deserialize(value.Value); + return Deserialize(value); } public static bool TryLoad(string key, out T data) @@ -47,7 +39,7 @@ public static bool TryLoad(string key, out T data) if (_data.TryGetValue(key, out var value)) { - data = Deserialize(value.Value); + data = Deserialize(value); hasKey = true; } else @@ -59,19 +51,27 @@ public static bool TryLoad(string key, out T data) return hasKey; } - public static bool HasKey(string key) => - _data.ContainsKey(key); + public static bool HasKey(string key) + { + return _data.ContainsKey(key); + } - public static void DeleteKey(string key) => + public static void DeleteKey(string key) + { _data.Remove(key); + } - public static void DeleteAll() => + public static void DeleteAll() + { _data.Clear(); + } public static void ChangeProfile(int profileIndex) { if (_currentProfileIndex == profileIndex) + { return; + } SaveFile(); @@ -83,10 +83,10 @@ public static void ChangeProfile(int profileIndex) [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)] private static void Setup() { - _container = Resources.Load("ToolBoxAssetsContainer"); - - _serializationContext = new SerializationContext { StringReferenceResolver = _container }; - _deserializationContext = new DeserializationContext { StringReferenceResolver = _container }; + Container = Resources.Load("ToolBoxAssetsContainer"); + _referenceResolver = new ReferenceResolver(Container); + _serializationContext = new SerializationContext(_referenceResolver); + _deserializationContext = new DeserializationContext(_referenceResolver); GeneratePath(); LoadFile(); @@ -114,30 +114,31 @@ private static void LoadFile() if (!File.Exists(_savePath)) { var fileStream = File.Create(_savePath); - fileStream?.Close(); + fileStream.Close(); } - - var bytes = File.ReadAllBytes(_savePath); - _data = Deserialize>(bytes) ?? new Dictionary(INITIAL_SIZE); + + _data = Deserialize>(File.ReadAllBytes(_savePath)) ?? new Dictionary(InitialSize); } - private static void GeneratePath() => - _savePath = Path.Combine(Application.persistentDataPath, $"{FILE_NAME}_{_currentProfileIndex}.data"); + private static void GeneratePath() + { + _savePath = Path.Combine(Application.persistentDataPath, $"{FileName}_{_currentProfileIndex.ToString()}.data"); + } private static byte[] Serialize(T data) { - var bytes = SerializationUtility.SerializeValue(data, DATA_FORMAT, _serializationContext); + var bytes = SerializationUtility.SerializeValue(data, DataFormat, _serializationContext); _serializationContext.ResetToDefault(); - _serializationContext.StringReferenceResolver = _container; + _serializationContext.IndexReferenceResolver = _referenceResolver; return bytes; } private static T Deserialize(byte[] bytes) { - var data = SerializationUtility.DeserializeValue(bytes, DATA_FORMAT, _deserializationContext); + var data = SerializationUtility.DeserializeValue(bytes, DataFormat, _deserializationContext); _deserializationContext.Reset(); - _deserializationContext.StringReferenceResolver = _container; + _deserializationContext.IndexReferenceResolver = _referenceResolver; return data; } diff --git a/DataSerializer/Item.cs b/DataSerializer/Item.cs deleted file mode 100644 index 37c8c32..0000000 --- a/DataSerializer/Item.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace ToolBox.Serialization -{ - internal sealed class Item - { - public byte[] Value = default; - } -} - diff --git a/DataSerializer/ReferenceResolver.cs b/DataSerializer/ReferenceResolver.cs new file mode 100644 index 0000000..eaaea74 --- /dev/null +++ b/DataSerializer/ReferenceResolver.cs @@ -0,0 +1,45 @@ +using ToolBox.Serialization.OdinSerializer; +using UnityEngine; + +namespace ToolBox.Serialization +{ + internal sealed class ReferenceResolver : IExternalIndexReferenceResolver + { + private readonly AssetsContainer _assetsContainer; + + public ReferenceResolver(AssetsContainer assetsContainer) + { + _assetsContainer = assetsContainer; + } + + public bool TryResolveReference(int index, out object value) + { + value = null; + + if (index == 0) + { + return false; + } + + var success = _assetsContainer.TryGetObject((ushort)index, out var obj); + + value = obj; + return success; + } + + public bool CanReference(object value, out int index) + { + index = 0; + + if (value is not Object obj) + { + return false; + } + + var success = _assetsContainer.TryGetId(obj, out var id); + + index = id; + return success; + } + } +} \ No newline at end of file diff --git a/DataSerializer/ReferenceResolver.cs.meta b/DataSerializer/ReferenceResolver.cs.meta new file mode 100644 index 0000000..098ca91 --- /dev/null +++ b/DataSerializer/ReferenceResolver.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9da1a027eed244359f845d3a8542fe5b +timeCreated: 1714835535 \ No newline at end of file diff --git a/DataSerializer/Resources/ToolBoxAssetsContainer.asset b/DataSerializer/Resources/ToolBoxAssetsContainer.asset index fcb8885..4fc771a 100644 --- a/DataSerializer/Resources/ToolBoxAssetsContainer.asset +++ b/DataSerializer/Resources/ToolBoxAssetsContainer.asset @@ -13,5 +13,4 @@ MonoBehaviour: m_Name: ToolBoxAssetsContainer m_EditorClassIdentifier: _savedAssets: [] - _paths: - - + _paths: [] diff --git a/OdinSerializer/Config.meta b/OdinSerializer/Config.meta index e506ab8..44f82eb 100644 --- a/OdinSerializer/Config.meta +++ b/OdinSerializer/Config.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 667fbd4059b7ed845814d594d1f73339 +guid: 7648cbc802ff4f8469db8f0e132e2d3b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Config/AssemblyBuildInfo.cs b/OdinSerializer/Config/AssemblyBuildInfo.cs index 1277031..f725f5b 100644 --- a/OdinSerializer/Config/AssemblyBuildInfo.cs +++ b/OdinSerializer/Config/AssemblyBuildInfo.cs @@ -84,21 +84,10 @@ public static class OdinAssemblyImportSettingsUtility public static readonly BuildTarget[] JITPlatforms = new BuildTarget[] { -#if UNITY_2017_3_OR_NEWER BuildTarget.StandaloneOSX, -#else - BuildTarget.StandaloneOSXIntel, - BuildTarget.StandaloneOSXIntel64, - BuildTarget.StandaloneOSXUniversal, -#endif - BuildTarget.StandaloneWindows, BuildTarget.StandaloneWindows64, - - BuildTarget.StandaloneLinux, BuildTarget.StandaloneLinux64, - BuildTarget.StandaloneLinuxUniversal, - BuildTarget.Android, }; diff --git a/OdinSerializer/Config/AssemblyBuildInfo.cs.meta b/OdinSerializer/Config/AssemblyBuildInfo.cs.meta index 760bd7c..436cf14 100644 --- a/OdinSerializer/Config/AssemblyBuildInfo.cs.meta +++ b/OdinSerializer/Config/AssemblyBuildInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9322a4845d4864a7366b2d12cfdace66 +guid: 60517bcc14b105518c4bed060f8e177c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Config/GlobalSerializationConfig.cs.meta b/OdinSerializer/Config/GlobalSerializationConfig.cs.meta index ab88de6..3e7a451 100644 --- a/OdinSerializer/Config/GlobalSerializationConfig.cs.meta +++ b/OdinSerializer/Config/GlobalSerializationConfig.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7c3153bd4a6d8925f7c188721b634625 +guid: e27adcd68a9f5970540201b87acffe99 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core.meta b/OdinSerializer/Core.meta index fe18d70..182a2c6 100644 --- a/OdinSerializer/Core.meta +++ b/OdinSerializer/Core.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ceec976cf868c98439615af7c4336514 +guid: eacc19cb04584174ca818fea8262f897 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/DataReaderWriters.meta b/OdinSerializer/Core/DataReaderWriters.meta index 8b21b9e..3fb9303 100644 --- a/OdinSerializer/Core/DataReaderWriters.meta +++ b/OdinSerializer/Core/DataReaderWriters.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c16fbaa26b274ee47b461c3da65e6da1 +guid: 31485af373d02a04db700b37228ac6b9 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/DataReaderWriters/BaseDataReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/BaseDataReader.cs.meta index dc0352e..ccf70c2 100644 --- a/OdinSerializer/Core/DataReaderWriters/BaseDataReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/BaseDataReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b3a28b87c296a13fb1d6f41b90e8bfa8 +guid: 6a6df883d7eacab7bdc93580fd5a6def MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs b/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs index 4e5c00f..098d661 100644 --- a/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs +++ b/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs @@ -83,6 +83,14 @@ public TwoWaySerializationBinder Binder /// protected int NodeDepth { get { return this.nodesLength; } } + /// + /// Gets the current nodes array. The amount of nodes contained in it is stored in the property. The remainder of the array's length is buffer space. + /// + /// + /// The current node array. + /// + protected NodeInfo[] NodesArray { get { return this.nodes; } } + /// /// Gets the current node, or if there is no current node. /// diff --git a/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs.meta index f8a39ea..81e9da4 100644 --- a/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/BaseDataReaderWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a5fadad2f0883669466229af151da847 +guid: 6c38bf9d13eca22d8f18363cda069de7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/BaseDataWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/BaseDataWriter.cs.meta index f9d8e48..324a721 100644 --- a/OdinSerializer/Core/DataReaderWriters/BaseDataWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/BaseDataWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6a0bdb593be61f77c2f874aadd8396cf +guid: 13f05c600ff322f5c6673d03df52427c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Binary.meta b/OdinSerializer/Core/DataReaderWriters/Binary.meta index 5b137fa..d7224dc 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary.meta +++ b/OdinSerializer/Core/DataReaderWriters/Binary.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 27e6361d4ae3d694fb40773b8f0d55bd +guid: b9969f5194a10c1429191da7b6c63fa5 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs index e72bbcf..e3bfe08 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs +++ b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs @@ -1836,7 +1836,7 @@ private string ReadStringValue() return null; } - string str = new string('\0', length); + string str = new string(' ', length); if (charSizeFlag == 0) { diff --git a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs.meta index 07031f1..bf8a5ff 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2145ada735eaec39ef4bd9620550fddc +guid: c6c669c41098d422943649a62871dea5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs index 3e35083..35a1d3a 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs +++ b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs @@ -1929,7 +1929,9 @@ private void UNSAFE_WriteToBuffer_4_Float32(float value) this.bufferIndex += 4; } - [MethodImpl((MethodImplOptions)0x100)] // Set aggressive inlining flag, for the runtimes that understand that + // Set a no inlining flag; as of issue #913, Unity 2022.3+ IL2CPP targeting at least Android and iOS will write this long + // incorrectly when this method is inlined, resulting in the fifth byte of the long being set to a (seemingly) random value. + [MethodImpl((MethodImplOptions)8)] // No-inlining private void UNSAFE_WriteToBuffer_8_Int64(long value) { fixed (byte* basePtr = this.buffer) @@ -1943,12 +1945,30 @@ private void UNSAFE_WriteToBuffer_8_Int64(long value) } else { - // We do a slower but safer int-by-int write instead - int* fromPtr = (int*)&value; - int* toPtr = (int*)(basePtr + this.bufferIndex); - - *toPtr++ = *fromPtr++; - *toPtr = *fromPtr; + // This used to be just using pointers directly, but this triggered subtle bugs + // in the xcode 15 and above compiler. So now we're wasting a few cycles to set + // the value into a union and then get the raw bytes from there. + // + // Our best theory for the xcode issue is that 1) it inlines this method despite it + // being marked to be not inlined, since it has passed through IL2CPP where that + // metadata has probably been lost or something. + // + // And 2), we think somebody at Apple got too clever about optimizing away the + // "passing of parameters" to inlined methods where it thinks that parameter is not + // being used anywhere in the inlined method. And then they forgot to count "taking + // the address of a parameter" as using it. So the serialized value would contain + // random garbage from the stack. + + SixtyFourBitValueToByteUnion union = default(SixtyFourBitValueToByteUnion); + union.longValue = value; + this.buffer[this.bufferIndex] = union.b0; + this.buffer[this.bufferIndex + 1] = union.b1; + this.buffer[this.bufferIndex + 2] = union.b2; + this.buffer[this.bufferIndex + 3] = union.b3; + this.buffer[this.bufferIndex + 4] = union.b4; + this.buffer[this.bufferIndex + 5] = union.b5; + this.buffer[this.bufferIndex + 6] = union.b6; + this.buffer[this.bufferIndex + 7] = union.b7; } } else @@ -1970,9 +1990,13 @@ private void UNSAFE_WriteToBuffer_8_Int64(long value) this.bufferIndex += 8; } - [MethodImpl((MethodImplOptions)0x100)] // Set aggressive inlining flag, for the runtimes that understand that - private void UNSAFE_WriteToBuffer_8_UInt64(ulong value) - { + // Set a no inlining flag; as of issue #913, Unity 2022.3+ IL2CPP targeting at least Android and iOS will write a long + // incorrectly when a method is inlined, resulting in the fifth byte of the long being set to a (seemingly) random value. + // This is only verified as being the case for UNSAFE_WriteToBuffer_8_Int64, but out of an abundance of caution, we are + // marking all > 8-byte writes as being non-inlinable. + [MethodImpl((MethodImplOptions)8)] // No-inlining + private void UNSAFE_WriteToBuffer_8_UInt64(ulong value) + { fixed (byte* basePtr = this.buffer) { if (BitConverter.IsLittleEndian) @@ -1984,12 +2008,19 @@ private void UNSAFE_WriteToBuffer_8_UInt64(ulong value) } else { - // We do a slower but safer int-by-int write instead - int* fromPtr = (int*)&value; - int* toPtr = (int*)(basePtr + this.bufferIndex); - - *toPtr++ = *fromPtr++; - *toPtr = *fromPtr; + // This used to be just raw pointers, but now it's a slightly slower union. + // See comment in UNSAFE_WriteToBuffer_8_Int64 for why we're doing this now. + + SixtyFourBitValueToByteUnion union = default(SixtyFourBitValueToByteUnion); + union.ulongValue = value; + this.buffer[this.bufferIndex] = union.b0; + this.buffer[this.bufferIndex + 1] = union.b1; + this.buffer[this.bufferIndex + 2] = union.b2; + this.buffer[this.bufferIndex + 3] = union.b3; + this.buffer[this.bufferIndex + 4] = union.b4; + this.buffer[this.bufferIndex + 5] = union.b5; + this.buffer[this.bufferIndex + 6] = union.b6; + this.buffer[this.bufferIndex + 7] = union.b7; } } else @@ -2011,8 +2042,12 @@ private void UNSAFE_WriteToBuffer_8_UInt64(ulong value) this.bufferIndex += 8; } - [MethodImpl((MethodImplOptions)0x100)] // Set aggressive inlining flag, for the runtimes that understand that - private void UNSAFE_WriteToBuffer_8_Float64(double value) + // Set a no inlining flag; as of issue #913, Unity 2022.3+ IL2CPP targeting at least Android and iOS will write a long + // incorrectly when a method is inlined, resulting in the fifth byte of the long being set to a (seemingly) random value. + // This is only verified as being the case for UNSAFE_WriteToBuffer_8_Int64, but out of an abundance of caution, we are + // marking all > 8-byte writes as being non-inlinable. + [MethodImpl((MethodImplOptions)8)] // No-inlining + private void UNSAFE_WriteToBuffer_8_Float64(double value) { fixed (byte* basePtr = this.buffer) { @@ -2025,12 +2060,19 @@ private void UNSAFE_WriteToBuffer_8_Float64(double value) } else { - // We do a slower but safer int-by-int write instead - int* fromPtr = (int*)&value; - int* toPtr = (int*)(basePtr + this.bufferIndex); - - *toPtr++ = *fromPtr++; - *toPtr = *fromPtr; + // This used to be just raw pointers, but now it's a slightly slower union. + // See comment in UNSAFE_WriteToBuffer_8_Int64 for why we're doing this now. + + SixtyFourBitValueToByteUnion union = default(SixtyFourBitValueToByteUnion); + union.doubleValue = value; + this.buffer[this.bufferIndex] = union.b0; + this.buffer[this.bufferIndex + 1] = union.b1; + this.buffer[this.bufferIndex + 2] = union.b2; + this.buffer[this.bufferIndex + 3] = union.b3; + this.buffer[this.bufferIndex + 4] = union.b4; + this.buffer[this.bufferIndex + 5] = union.b5; + this.buffer[this.bufferIndex + 6] = union.b6; + this.buffer[this.bufferIndex + 7] = union.b7; } } else @@ -2052,8 +2094,12 @@ private void UNSAFE_WriteToBuffer_8_Float64(double value) this.bufferIndex += 8; } - [MethodImpl((MethodImplOptions)0x100)] // Set aggressive inlining flag, for the runtimes that understand that - private void UNSAFE_WriteToBuffer_16_Decimal(decimal value) + // Set a no inlining flag; as of issue #913, Unity 2022.3+ IL2CPP targeting at least Android and iOS will write a long + // incorrectly when a method is inlined, resulting in the fifth byte of the long being set to a (seemingly) random value. + // This is only verified as being the case for UNSAFE_WriteToBuffer_8_Int64, but out of an abundance of caution, we are + // marking all > 8-byte writes as being non-inlinable. + [MethodImpl((MethodImplOptions)8)] // No-inlining + private void UNSAFE_WriteToBuffer_16_Decimal(decimal value) { fixed (byte* basePtr = this.buffer) { @@ -2066,14 +2112,27 @@ private void UNSAFE_WriteToBuffer_16_Decimal(decimal value) } else { - // We do a slower but safer int-by-int write instead - int* fromPtr = (int*)&value; - int* toPtr = (int*)(basePtr + this.bufferIndex); - - *toPtr++ = *fromPtr++; - *toPtr++ = *fromPtr++; - *toPtr++ = *fromPtr++; - *toPtr = *fromPtr; + // This used to be just raw pointers, but now it's a slightly slower union. + // See comment in UNSAFE_WriteToBuffer_8_Int64 for why we're doing this now. + + OneTwentyEightBitValueToByteUnion union = default(OneTwentyEightBitValueToByteUnion); + union.decimalValue = value; + this.buffer[this.bufferIndex] = union.b0; + this.buffer[this.bufferIndex + 1] = union.b1; + this.buffer[this.bufferIndex + 2] = union.b2; + this.buffer[this.bufferIndex + 3] = union.b3; + this.buffer[this.bufferIndex + 4] = union.b4; + this.buffer[this.bufferIndex + 5] = union.b5; + this.buffer[this.bufferIndex + 6] = union.b6; + this.buffer[this.bufferIndex + 7] = union.b7; + this.buffer[this.bufferIndex + 8] = union.b8; + this.buffer[this.bufferIndex + 9] = union.b9; + this.buffer[this.bufferIndex + 10] = union.b10; + this.buffer[this.bufferIndex + 11] = union.b11; + this.buffer[this.bufferIndex + 12] = union.b12; + this.buffer[this.bufferIndex + 13] = union.b13; + this.buffer[this.bufferIndex + 14] = union.b14; + this.buffer[this.bufferIndex + 15] = union.b15; } } else @@ -2101,10 +2160,14 @@ private void UNSAFE_WriteToBuffer_16_Decimal(decimal value) } this.bufferIndex += 16; - } + } - [MethodImpl((MethodImplOptions)0x100)] // Set aggressive inlining flag, for the runtimes that understand that - private void UNSAFE_WriteToBuffer_16_Guid(Guid value) + // Set a no inlining flag; as of issue #913, Unity 2022.3+ IL2CPP targeting at least Android and iOS will write a long + // incorrectly when a method is inlined, resulting in the fifth byte of the long being set to a (seemingly) random value. + // This is only verified as being the case for UNSAFE_WriteToBuffer_8_Int64, but out of an abundance of caution, we are + // marking all > 8-byte writes as being non-inlinable. + [MethodImpl((MethodImplOptions)8)] // No-inlining + private void UNSAFE_WriteToBuffer_16_Guid(Guid value) { // First 10 bytes of a guid are always little endian // Last 6 bytes depend on architecture endianness @@ -2123,14 +2186,27 @@ private void UNSAFE_WriteToBuffer_16_Guid(Guid value) } else { - // We do a slower but safer int-by-int write instead - int* fromPtr = (int*)&value; - int* toPtr = (int*)(basePtr + this.bufferIndex); - - *toPtr++ = *fromPtr++; - *toPtr++ = *fromPtr++; - *toPtr++ = *fromPtr++; - *toPtr = *fromPtr; + // This used to be just raw pointers, but now it's a slightly slower union. + // See comment in UNSAFE_WriteToBuffer_8_Int64 for why we're doing this now. + + OneTwentyEightBitValueToByteUnion union = default(OneTwentyEightBitValueToByteUnion); + union.guidValue = value; + this.buffer[this.bufferIndex] = union.b0; + this.buffer[this.bufferIndex + 1] = union.b1; + this.buffer[this.bufferIndex + 2] = union.b2; + this.buffer[this.bufferIndex + 3] = union.b3; + this.buffer[this.bufferIndex + 4] = union.b4; + this.buffer[this.bufferIndex + 5] = union.b5; + this.buffer[this.bufferIndex + 6] = union.b6; + this.buffer[this.bufferIndex + 7] = union.b7; + this.buffer[this.bufferIndex + 8] = union.b8; + this.buffer[this.bufferIndex + 9] = union.b9; + this.buffer[this.bufferIndex + 10] = union.b10; + this.buffer[this.bufferIndex + 11] = union.b11; + this.buffer[this.bufferIndex + 12] = union.b12; + this.buffer[this.bufferIndex + 13] = union.b13; + this.buffer[this.bufferIndex + 14] = union.b14; + this.buffer[this.bufferIndex + 15] = union.b15; } } else @@ -2196,5 +2272,77 @@ private bool TryEnsureBufferSpace(int space) return true; } + + // Used for safe unaligned writes of 64-bit values + [StructLayout(LayoutKind.Explicit, Size = 8)] + private struct SixtyFourBitValueToByteUnion + { + [FieldOffset(0)] + public byte b0; + [FieldOffset(1)] + public byte b1; + [FieldOffset(2)] + public byte b2; + [FieldOffset(3)] + public byte b3; + [FieldOffset(4)] + public byte b4; + [FieldOffset(5)] + public byte b5; + [FieldOffset(6)] + public byte b6; + [FieldOffset(7)] + public byte b7; + [FieldOffset(0)] + public double doubleValue; + [FieldOffset(0)] + public ulong ulongValue; + [FieldOffset(0)] + public long longValue; + } + + // Used for safe unaligned writes of 128-bit values + [StructLayout(LayoutKind.Explicit, Size = 16)] + private struct OneTwentyEightBitValueToByteUnion + { + [FieldOffset(0)] + public byte b0; + [FieldOffset(1)] + public byte b1; + [FieldOffset(2)] + public byte b2; + [FieldOffset(3)] + public byte b3; + [FieldOffset(4)] + public byte b4; + [FieldOffset(5)] + public byte b5; + [FieldOffset(6)] + public byte b6; + [FieldOffset(7)] + public byte b7; + [FieldOffset(8)] + public byte b8; + [FieldOffset(9)] + public byte b9; + [FieldOffset(10)] + public byte b10; + [FieldOffset(11)] + public byte b11; + [FieldOffset(12)] + public byte b12; + [FieldOffset(13)] + public byte b13; + [FieldOffset(14)] + public byte b14; + [FieldOffset(15)] + public byte b15; + + [FieldOffset(0)] + public Guid guidValue; + [FieldOffset(0)] + public decimal decimalValue; + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs.meta index ee3d87c..e8b99bd 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryDataWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b2cdba3591ddb38d3f3ae2235753dd9a +guid: 3d4b0cc25d64f075a540ae788eacb61f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryEntryType.cs.meta b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryEntryType.cs.meta index d4b03c0..8856953 100644 --- a/OdinSerializer/Core/DataReaderWriters/Binary/BinaryEntryType.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Binary/BinaryEntryType.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c3fff16d4053796cc4202089d889afec +guid: 1d38d920e7e3c179f67633df470f39f1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/IDataReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/IDataReader.cs.meta index 64b6ae6..a128fea 100644 --- a/OdinSerializer/Core/DataReaderWriters/IDataReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/IDataReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9ae0c91679f50ee55d2d5a61a04509b7 +guid: 2fb2fd49c4d02c49743c2b88a46815f5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/IDataWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/IDataWriter.cs.meta index 3ca8556..a6ff66f 100644 --- a/OdinSerializer/Core/DataReaderWriters/IDataWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/IDataWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 380695d0dacb2f71e65106cfa677a539 +guid: 4bb83480cb4d37ae671bab5ba03e73ff MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Json.meta b/OdinSerializer/Core/DataReaderWriters/Json.meta index cb00312..5217d6b 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json.meta +++ b/OdinSerializer/Core/DataReaderWriters/Json.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 28900961a9dd22544841e68af2927571 +guid: cbc0968453c9b4f478b0edc64cb8b67e folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/DataReaderWriters/Json/JsonConfig.cs.meta b/OdinSerializer/Core/DataReaderWriters/Json/JsonConfig.cs.meta index 6f27a3a..cddc587 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json/JsonConfig.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Json/JsonConfig.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 99efe0a2d81cb056b01cbb0e6d5805d3 +guid: 5aad8a6a393b956b1aaba423782a9192 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Json/JsonDataReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/Json/JsonDataReader.cs.meta index 5402e5d..989a221 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json/JsonDataReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Json/JsonDataReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5448a06140a761ebf7e9d1167943f27d +guid: 656dd50b0d0a851601fd9dfd48e94b87 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Json/JsonDataWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/Json/JsonDataWriter.cs.meta index 4578a4e..9a0eef2 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json/JsonDataWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Json/JsonDataWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 000c7eaaeb9b1eed446e4fe9780a7802 +guid: 9cc6e09389ccab958852a7a2d80225d0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs b/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs index 59ca995..36d2852 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs +++ b/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs @@ -407,21 +407,21 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out name = new string(this.buffer, 0, valueSeparatorIndex); } - if (string.Equals(name, JsonConfig.REGULAR_ARRAY_CONTENT_SIG, StringComparison.InvariantCulture) && hintEntry == EntryType.StartOfArray) + if (StringComparer.Ordinal.Equals(name, JsonConfig.REGULAR_ARRAY_CONTENT_SIG) && hintEntry == EntryType.StartOfArray) { valueContent = null; entry = EntryType.StartOfArray; return; } - if (string.Equals(name, JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG, StringComparison.InvariantCulture) && hintEntry == EntryType.StartOfArray) + if (StringComparer.Ordinal.Equals(name, JsonConfig.PRIMITIVE_ARRAY_CONTENT_SIG) && hintEntry == EntryType.StartOfArray) { valueContent = null; entry = EntryType.PrimitiveArray; return; } - if (string.Equals(name, JsonConfig.INTERNAL_REF_SIG, StringComparison.InvariantCulture)) + if (StringComparer.Ordinal.Equals(name, JsonConfig.INTERNAL_REF_SIG)) { // It's an object reference without a name // The content is the whole buffer @@ -431,7 +431,7 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out return; } - if (string.Equals(name, JsonConfig.EXTERNAL_INDEX_REF_SIG, StringComparison.InvariantCulture)) + if (StringComparer.Ordinal.Equals(name, JsonConfig.EXTERNAL_INDEX_REF_SIG)) { // It's an external index reference without a name // The content is the whole buffer @@ -441,7 +441,7 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out return; } - if (string.Equals(name, JsonConfig.EXTERNAL_GUID_REF_SIG, StringComparison.InvariantCulture)) + if (StringComparer.Ordinal.Equals(name, JsonConfig.EXTERNAL_GUID_REF_SIG)) { // It's an external guid reference without a name // The content is the whole buffer @@ -451,7 +451,7 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out return; } - if (string.Equals(name, JsonConfig.EXTERNAL_STRING_REF_SIG_OLD, StringComparison.InvariantCulture)) + if (StringComparer.Ordinal.Equals(name, JsonConfig.EXTERNAL_STRING_REF_SIG_OLD)) { // It's an external guid reference without a name, of the old broken kind // The content is the whole buffer @@ -461,7 +461,7 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out return; } - if (string.Equals(name, JsonConfig.EXTERNAL_STRING_REF_SIG_FIXED, StringComparison.InvariantCulture)) + if (StringComparer.Ordinal.Equals(name, JsonConfig.EXTERNAL_STRING_REF_SIG_FIXED)) { // It's an external guid reference without a name, of the new non-broken kind // The content is the buffer, unquoted and unescaped @@ -483,13 +483,13 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out if (valueContent != null) { // We can now try to see what the value content actually is, and as such determine the type of the entry - if (string.Equals(name, JsonConfig.REGULAR_ARRAY_LENGTH_SIG, StringComparison.InvariantCulture)) // This is a special case for the length entry that must always come before an array + if (StringComparer.Ordinal.Equals(name, JsonConfig.REGULAR_ARRAY_LENGTH_SIG)) // This is a special case for the length entry that must always come before an array { entry = EntryType.StartOfArray; return; } - if (string.Equals(name, JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG, StringComparison.InvariantCulture)) // This is a special case for the length entry that must always come before an array + if (StringComparer.Ordinal.Equals(name, JsonConfig.PRIMITIVE_ARRAY_LENGTH_SIG)) // This is a special case for the length entry that must always come before an array { entry = EntryType.PrimitiveArray; return; @@ -501,52 +501,52 @@ private void ParseEntryFromBuffer(out string name, out string valueContent, out return; } - if (string.Equals(valueContent, "null", StringComparison.InvariantCultureIgnoreCase)) + if (StringComparer.OrdinalIgnoreCase.Equals(valueContent, "null")) { entry = EntryType.Null; return; } - else if (string.Equals(valueContent, "{", StringComparison.InvariantCulture)) + else if (StringComparer.Ordinal.Equals(valueContent, "{")) { entry = EntryType.StartOfNode; return; } - else if (string.Equals(valueContent, "}", StringComparison.InvariantCulture)) + else if (StringComparer.Ordinal.Equals(valueContent, "}")) { entry = EntryType.EndOfNode; return; } - else if (string.Equals(valueContent, "[", StringComparison.InvariantCulture)) + else if (StringComparer.Ordinal.Equals(valueContent, "[")) { entry = EntryType.StartOfArray; return; } - else if (string.Equals(valueContent, "]", StringComparison.InvariantCulture)) + else if (StringComparer.Ordinal.Equals(valueContent, "]")) { entry = EntryType.EndOfArray; return; } - else if (valueContent.StartsWith(JsonConfig.INTERNAL_REF_SIG, StringComparison.InvariantCulture)) + else if (valueContent.StartsWith(JsonConfig.INTERNAL_REF_SIG, StringComparison.Ordinal)) { entry = EntryType.InternalReference; return; } - else if (valueContent.StartsWith(JsonConfig.EXTERNAL_INDEX_REF_SIG, StringComparison.InvariantCulture)) + else if (valueContent.StartsWith(JsonConfig.EXTERNAL_INDEX_REF_SIG, StringComparison.Ordinal)) { entry = EntryType.ExternalReferenceByIndex; return; } - else if (valueContent.StartsWith(JsonConfig.EXTERNAL_GUID_REF_SIG, StringComparison.InvariantCulture)) + else if (valueContent.StartsWith(JsonConfig.EXTERNAL_GUID_REF_SIG, StringComparison.Ordinal)) { entry = EntryType.ExternalReferenceByGuid; return; } - else if (valueContent.StartsWith(JsonConfig.EXTERNAL_STRING_REF_SIG_OLD, StringComparison.InvariantCulture)) + else if (valueContent.StartsWith(JsonConfig.EXTERNAL_STRING_REF_SIG_OLD, StringComparison.Ordinal)) { entry = EntryType.ExternalReferenceByString; return; } - else if (valueContent.StartsWith(JsonConfig.EXTERNAL_STRING_REF_SIG_FIXED, StringComparison.InvariantCulture)) + else if (valueContent.StartsWith(JsonConfig.EXTERNAL_STRING_REF_SIG_FIXED, StringComparison.Ordinal)) { entry = EntryType.ExternalReferenceByString; return; @@ -666,7 +666,7 @@ private char ReadCharIntoBuffer() private EntryType? GuessPrimitiveType(string content) { // This method tries to guess what kind of primitive type the current entry is, as cheaply as possible - if (string.Equals(content, "null", StringComparison.InvariantCultureIgnoreCase)) + if (StringComparer.OrdinalIgnoreCase.Equals(content, "null")) { return EntryType.Null; } @@ -682,7 +682,7 @@ private char ReadCharIntoBuffer() { return EntryType.FloatingPoint; } - else if (string.Equals(content, "true", StringComparison.InvariantCultureIgnoreCase) || string.Equals(content, "false", StringComparison.InvariantCultureIgnoreCase)) + else if (StringComparer.OrdinalIgnoreCase.Equals(content, "true") || StringComparer.OrdinalIgnoreCase.Equals(content, "false")) { return EntryType.Boolean; } diff --git a/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs.meta index 317e369..5cc0457 100644 --- a/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/Json/JsonTextReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a5c00f6f0fb003acca7c5150a8960d76 +guid: 1b75e9581b8482dc665ef4d91015489b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/SerializationNodes.meta b/OdinSerializer/Core/DataReaderWriters/SerializationNodes.meta index d17f51a..edb713e 100644 --- a/OdinSerializer/Core/DataReaderWriters/SerializationNodes.meta +++ b/OdinSerializer/Core/DataReaderWriters/SerializationNodes.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ab8776ff50a88e3459b0d80ad741ac11 +guid: 43e67c9a6f21b804794bc9fc997d7ccb folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNode.cs.meta b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNode.cs.meta index 60d3ccc..e311571 100644 --- a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNode.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNode.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1dec027230f3c3d805fc0cc1e2c35b50 +guid: 67f46edceb9df456c41c12683691adc2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReader.cs.meta b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReader.cs.meta index bbd5ca4..5a76e12 100644 --- a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReader.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReader.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c1c7a36e246963247ea1a291e9d5f76a +guid: 758990971e1300f2114e56626afbf6f0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReaderWriterConfig.cs.meta b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReaderWriterConfig.cs.meta index 79ed4df..62bd853 100644 --- a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReaderWriterConfig.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataReaderWriterConfig.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3a6f2c050c88a20cb98347c2f7a7e9e2 +guid: f90ee7d073cabc755bada1384e1e2ee6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataWriter.cs.meta b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataWriter.cs.meta index 60a907d..f71c563 100644 --- a/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataWriter.cs.meta +++ b/OdinSerializer/Core/DataReaderWriters/SerializationNodes/SerializationNodeDataWriter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 21e9ca2d8136f3cde0a7884079cc8a72 +guid: 530343c9e7f4c2a32232db8d96dc456e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators.meta b/OdinSerializer/Core/FormatterLocators.meta index 94eaadf..5f877fd 100644 --- a/OdinSerializer/Core/FormatterLocators.meta +++ b/OdinSerializer/Core/FormatterLocators.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 24acf931458625f459557bfb27710225 +guid: 1b2c84d8e9f91304bb62f61f9e9098e2 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs index 5d01884..52cb024 100644 --- a/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs @@ -26,7 +26,7 @@ namespace ToolBox.Serialization.OdinSerializer internal class ArrayFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { if (!type.IsArray) { @@ -34,20 +34,61 @@ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializatio return false; } + var elementType = type.GetElementType(); + if (type.GetArrayRank() == 1) { - if (FormatterUtilities.IsPrimitiveArrayType(type.GetElementType())) + if (FormatterUtilities.IsPrimitiveArrayType(elementType)) { - formatter = (IFormatter)Activator.CreateInstance(typeof(PrimitiveArrayFormatter<>).MakeGenericType(type.GetElementType())); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(PrimitiveArrayFormatter<>).MakeGenericType(elementType)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakPrimitiveArrayFormatter(type, elementType); + } + else throw; + } } else { - formatter = (IFormatter)Activator.CreateInstance(typeof(ArrayFormatter<>).MakeGenericType(type.GetElementType())); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(ArrayFormatter<>).MakeGenericType(elementType)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakArrayFormatter(type, elementType); + } + else throw; + } } } else { - formatter = (IFormatter)Activator.CreateInstance(typeof(MultiDimensionalArrayFormatter<,>).MakeGenericType(type, type.GetElementType())); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(MultiDimensionalArrayFormatter<,>).MakeGenericType(type, type.GetElementType())); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakMultiDimensionalArrayFormatter(type, elementType); + } + else throw; + } } return true; diff --git a/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs.meta index b8528ae..7a2df40 100644 --- a/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/ArrayFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b21b2925ccb4dd7ce1159bed33546089 +guid: c758adcd93e7b5ec518f3747f3136fa3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs index 2cb9438..148712b 100644 --- a/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs @@ -22,11 +22,12 @@ namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; internal class DelegateFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { if (!typeof(Delegate).IsAssignableFrom(type)) { @@ -34,7 +35,21 @@ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializatio return false; } - formatter = (IFormatter)Activator.CreateInstance(typeof(DelegateFormatter<>).MakeGenericType(type)); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(DelegateFormatter<>).MakeGenericType(type)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakDelegateFormatter(type); + } + else throw; + } + return true; } } diff --git a/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs.meta index a8a6d86..f31f1c8 100644 --- a/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/DelegateFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cd6d529a6612c50428aff2f647b7b618 +guid: dd5485e737c9a4e531ea3f0711f1c310 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs index fd9201d..21af1d8 100644 --- a/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs @@ -34,15 +34,18 @@ namespace ToolBox.Serialization.OdinSerializer public static class FormatterLocator { - private static readonly object LOCK = new object(); + private static readonly object StrongFormatters_LOCK = new object(); + private static readonly object WeakFormatters_LOCK = new object(); private static readonly Dictionary FormatterInstances = new Dictionary(FastTypeComparer.Instance); - private static readonly DoubleLookupDictionary TypeFormatterMap = new DoubleLookupDictionary(FastTypeComparer.Instance, ReferenceEqualityComparer.Default); + private static readonly DoubleLookupDictionary StrongTypeFormatterMap = new DoubleLookupDictionary(FastTypeComparer.Instance, ReferenceEqualityComparer.Default); + private static readonly DoubleLookupDictionary WeakTypeFormatterMap = new DoubleLookupDictionary(FastTypeComparer.Instance, ReferenceEqualityComparer.Default); private struct FormatterInfo { public Type FormatterType; public Type TargetType; + public Type WeakFallbackType; public bool AskIfCanFormatTypes; public int Priority; } @@ -117,6 +120,7 @@ static FormatterLocator() FormatterInfos.Add(new FormatterInfo() { FormatterType = attr.FormatterType, + WeakFallbackType = attr.WeakFallback, TargetType = attr.FormatterType.GetArgumentsOfInheritedOpenGenericInterface(typeof(IFormatter<>))[0], AskIfCanFormatTypes = typeof(IAskIfCanFormatTypes).IsAssignableFrom(attr.FormatterType), Priority = attr.Priority @@ -222,7 +226,7 @@ public static event Func FormatterResolve /// public static IFormatter GetFormatter(ISerializationPolicy policy) { - return (IFormatter)GetFormatter(typeof(T), policy); + return (IFormatter)GetFormatter(typeof(T), policy, false); } /// @@ -236,6 +240,23 @@ public static IFormatter GetFormatter(ISerializationPolicy policy) /// The type argument is null. public static IFormatter GetFormatter(Type type, ISerializationPolicy policy) { + return GetFormatter(type, policy, true); + } + + /// + /// Gets a formatter for a given type. + /// + /// The type to get a formatter for. + /// The serialization policy to use if a formatter has to be emitted. If null, is used. + /// Whether to allow the use of weak fallback formatters which do not implement the strongly typed , but which conversely do not need to have had AOT support generated. + /// + /// A formatter for the given type. + /// + /// The type argument is null. + public static IFormatter GetFormatter(Type type, ISerializationPolicy policy, bool allowWeakFallbackFormatters) + { + IFormatter result; + if (type == null) { throw new ArgumentNullException("type"); @@ -246,18 +267,19 @@ public static IFormatter GetFormatter(Type type, ISerializationPolicy policy) policy = SerializationPolicies.Strict; } - IFormatter result; + var lockObj = allowWeakFallbackFormatters ? WeakFormatters_LOCK : StrongFormatters_LOCK; + var formatterMap = allowWeakFallbackFormatters ? WeakTypeFormatterMap : StrongTypeFormatterMap; - lock (LOCK) + lock (lockObj) { - if (TypeFormatterMap.TryGetInnerValue(type, policy, out result) == false) + if (formatterMap.TryGetInnerValue(type, policy, out result) == false) { // System.ExecutionEngineException is marked obsolete in .NET 4.6. // That's all very good for .NET, but Unity still uses it, and that means we use it as well! #pragma warning disable 618 try { - result = CreateFormatter(type, policy); + result = CreateFormatter(type, policy, allowWeakFallbackFormatters); } catch (TargetInvocationException ex) { @@ -286,7 +308,7 @@ public static IFormatter GetFormatter(Type type, ISerializationPolicy policy) LogAOTError(type, ex); } - TypeFormatterMap.AddInner(type, policy, result); + formatterMap.AddInner(type, policy, result); #pragma warning restore 618 } } @@ -326,7 +348,7 @@ private static void LogAOTError(Type type, Exception ex) "and ADD THEM MANUALLY): \n\n" + string.Join("\n", types) + "\n\nIF ALL THE TYPES ARE IN THE SUPPORT LIST AND YOU STILL GET THIS ERROR, PLEASE REPORT AN ISSUE." + "The exception contained the following message: \n" + ex.Message); - throw new SerializationAbortException("AOT formatter support was missing for type '" + type.GetNiceFullName() + "'."); + throw new SerializationAbortException("AOT formatter support was missing for type '" + type.GetNiceFullName() + "'.", ex); } private static IEnumerable GetAllPossibleMissingAOTTypes(Type type) @@ -364,7 +386,7 @@ internal static List GetAllCompatiblePredefinedFormatters(Type type, try { IFormatter result; - if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, out result)) + if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, true, out result)) { formatters.Add(result); } @@ -440,7 +462,7 @@ internal static List GetAllCompatiblePredefinedFormatters(Type type, try { IFormatter result; - if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, out result)) + if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, true, out result)) { formatters.Add(result); } @@ -470,7 +492,7 @@ internal static List GetAllCompatiblePredefinedFormatters(Type type, return formatters; } - private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy) + private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy, bool allowWeakFormatters) { if (FormatterUtilities.IsPrimitiveType(type)) { @@ -483,7 +505,7 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy try { IFormatter result; - if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, out result)) + if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.BeforeRegisteredFormatters, policy, allowWeakFormatters, out result)) { return result; } @@ -514,6 +536,8 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy var info = FormatterInfos[i]; Type formatterType = null; + Type weakFallbackType = null; + Type[] genericFormatterArgs = null; if (type == info.TargetType) { @@ -525,7 +549,7 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy if (info.FormatterType.TryInferGenericParameters(out inferredArgs, type)) { - formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(inferredArgs); + genericFormatterArgs = inferredArgs; } } else if (type.IsGenericType && info.FormatterType.IsGenericType && info.TargetType.IsGenericType && type.GetGenericTypeDefinition() == info.TargetType.GetGenericTypeDefinition()) @@ -534,13 +558,66 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy if (info.FormatterType.AreGenericConstraintsSatisfiedBy(args)) { - formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(args); + genericFormatterArgs = args; } } + if (formatterType == null && genericFormatterArgs != null) + { + formatterType = info.FormatterType.GetGenericTypeDefinition().MakeGenericType(genericFormatterArgs); + weakFallbackType = info.WeakFallbackType; + } + if (formatterType != null) { - var instance = GetFormatterInstance(formatterType); + IFormatter instance = null; + + bool aotError = false; + Exception aotEx = null; + + try + { + instance = GetFormatterInstance(formatterType); + } +#pragma warning disable 618 + catch (TargetInvocationException ex) + { + aotError = true; + aotEx = ex; + } + catch (TypeInitializationException ex) + { + aotError = true; + aotEx = ex; + } + catch (ExecutionEngineException ex) + { + aotError = true; + aotEx = ex; + } +#pragma warning restore 618 + + if (aotError && !EmitUtilities.CanEmit && allowWeakFormatters) + { + if (weakFallbackType != null) + { + instance = (IFormatter)Activator.CreateInstance(weakFallbackType, type); + } + + if (instance == null) + { + string argsStr = ""; + + for (int j = 0; j < genericFormatterArgs.Length; j++) + { + if (j > 0) argsStr = argsStr + ", "; + argsStr = argsStr + genericFormatterArgs[j].GetNiceFullName(); + } + + Debug.LogError("No AOT support was generated for serialization formatter type '" + info.FormatterType.GetNiceFullName() + "' for the generic arguments <" + argsStr + ">, and no weak fallback formatter was specified."); + throw aotEx; + } + } if (instance == null) continue; @@ -559,7 +636,7 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy try { IFormatter result; - if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, out result)) + if (FormatterLocators[i].LocatorInstance.TryGetFormatter(type, FormatterLocationStep.AfterRegisteredFormatters, policy, allowWeakFormatters, out result)) { return result; } @@ -599,7 +676,28 @@ private static IFormatter CreateFormatter(Type type, ISerializationPolicy policy } // Finally, we fall back to a reflection-based formatter if nothing else has been found - return (IFormatter)Activator.CreateInstance(typeof(ReflectionFormatter<>).MakeGenericType(type)); + try + { + return (IFormatter)Activator.CreateInstance(typeof(ReflectionFormatter<>).MakeGenericType(type)); + } + + catch (TargetInvocationException ex) + { + if (allowWeakFormatters) return new WeakReflectionFormatter(type); + throw ex; + } + catch (TypeInitializationException ex) + { + if (allowWeakFormatters) return new WeakReflectionFormatter(type); + throw ex; + } +#pragma warning disable CS0618 // Type or member is obsolete + catch (ExecutionEngineException ex) +#pragma warning restore CS0618 // Type or member is obsolete + { + if (allowWeakFormatters) return new WeakReflectionFormatter(type); + throw ex; + } } private static IFormatter GetFormatterInstance(Type type) diff --git a/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs.meta index d472356..514a8c6 100644 --- a/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/FormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5e18ffcb0216a7f984f1d7ad209b348d +guid: 5ea16c612a418e93cbc62f739909b337 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs index def8dd0..9b7cb55 100644 --- a/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs @@ -22,11 +22,12 @@ namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; internal class GenericCollectionFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { Type elementType; if (step != FormatterLocationStep.AfterRegisteredFormatters || !GenericCollectionFormatter.CanFormat(type, out elementType)) @@ -35,7 +36,21 @@ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializatio return false; } - formatter = (IFormatter)Activator.CreateInstance(typeof(GenericCollectionFormatter<,>).MakeGenericType(type, elementType)); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(GenericCollectionFormatter<,>).MakeGenericType(type, elementType)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakGenericCollectionFormatter(type, elementType); + } + else throw; + } + return true; } } diff --git a/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs.meta index 9a9af6a..baafe76 100644 --- a/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/GenericCollectionFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b7312845a5a6d1c6881ff6d08f2f3b96 +guid: 417cf33d6f3e5ea771dcde6e1473ce5f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs index 1b4d80f..0e7fb18 100644 --- a/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs @@ -22,6 +22,6 @@ namespace ToolBox.Serialization.OdinSerializer public interface IFormatterLocator { - bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter); + bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter); } } \ No newline at end of file diff --git a/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs.meta index f5e4f9d..a00524a 100644 --- a/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/IFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b5f83b4bd7efd1d30b22178732beac49 +guid: 3b0a83e867ab7851d21efb14c43a39e1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs index d93301d..263dcb9 100644 --- a/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs @@ -24,12 +24,11 @@ namespace ToolBox.Serialization.OdinSerializer { using Utilities; using System; - using System.Collections.Generic; using System.Runtime.Serialization; internal class ISerializableFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { if (step != FormatterLocationStep.AfterRegisteredFormatters || !typeof(ISerializable).IsAssignableFrom(type)) { @@ -37,7 +36,21 @@ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializatio return false; } - formatter = (IFormatter)Activator.CreateInstance(typeof(SerializableFormatter<>).MakeGenericType(type)); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(SerializableFormatter<>).MakeGenericType(type)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakSerializableFormatter(type); + } + else throw; + } + return true; } } diff --git a/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs.meta index a5ebb32..6777c5b 100644 --- a/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/ISerializableFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a883d22e750c0e39cb28c2aef1efb232 +guid: 4c1a8a20bb7aaed4055b27ae6e97acad MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs index 9097cca..2f60a6c 100644 --- a/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs @@ -27,7 +27,7 @@ namespace ToolBox.Serialization.OdinSerializer internal class SelfFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { formatter = null; @@ -36,7 +36,21 @@ public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializatio if ((step == FormatterLocationStep.BeforeRegisteredFormatters && type.IsDefined()) || step == FormatterLocationStep.AfterRegisteredFormatters) { - formatter = (IFormatter)Activator.CreateInstance(typeof(SelfFormatterFormatter<>).MakeGenericType(type)); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(SelfFormatterFormatter<>).MakeGenericType(type)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakSelfFormatterFormatter(type); + } + else throw; + } + return true; } diff --git a/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs.meta index 38c640d..b81dbf0 100644 --- a/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/SelfFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6495cb68ebd81b850f7179e2641d82be +guid: 035cee6f463d09fb606d915e4a3abb47 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs b/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs index 09ee356..3d11f1b 100644 --- a/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs +++ b/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs @@ -26,7 +26,7 @@ namespace ToolBox.Serialization.OdinSerializer internal class TypeFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { if (!typeof(Type).IsAssignableFrom(type)) { diff --git a/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs.meta b/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs.meta index da22fa1..929bc1e 100644 --- a/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs.meta +++ b/OdinSerializer/Core/FormatterLocators/TypeFormatterLocator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a6815b3cf64e185c1d9a2a336296fa78 +guid: c650062cf8d5162f8ccc662edcf718fa MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters.meta b/OdinSerializer/Core/Formatters.meta index 10b1fe3..f7a2c2e 100644 --- a/OdinSerializer/Core/Formatters.meta +++ b/OdinSerializer/Core/Formatters.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 175bf29b05f2e2840b87ab419d854ef0 +guid: 170e63f048f0ecc42b7052accf063bf1 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/Formatters/ArrayFormatter.cs b/OdinSerializer/Core/Formatters/ArrayFormatter.cs index 080af79..3afb51b 100644 --- a/OdinSerializer/Core/Formatters/ArrayFormatter.cs +++ b/OdinSerializer/Core/Formatters/ArrayFormatter.cs @@ -18,6 +18,8 @@ namespace ToolBox.Serialization.OdinSerializer { + using System; + /// /// Formatter for all non-primitive one-dimensional arrays. /// @@ -106,4 +108,83 @@ protected override void SerializeImplementation(ref T[] value, IDataWriter write } } } + + public sealed class WeakArrayFormatter : WeakBaseFormatter + { + private readonly Serializer ValueReaderWriter; + private readonly Type ElementType; + + public WeakArrayFormatter(Type arrayType, Type elementType) : base(arrayType) + { + this.ValueReaderWriter = Serializer.Get(elementType); + this.ElementType = elementType; + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + long length; + reader.EnterArray(out length); + + Array array = Array.CreateInstance(this.ElementType, length); + value = array; + + // We must remember to register the array reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + // There aren't any OnDeserializing callbacks on arrays. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + array.SetValue(ValueReaderWriter.ReadValueWeak(reader), i); + + if (reader.PeekEntry(out name) == EntryType.EndOfStream) + { + break; + } + } + + reader.ExitArray(); + } + else + { + reader.SkipEntry(); + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + Array array = (Array)value; + + try + { + int length = array.Length; + writer.BeginArrayNode(length); + + for (int i = 0; i < length; i++) + { + ValueReaderWriter.WriteValueWeak(array.GetValue(i), writer); + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/ArrayFormatter.cs.meta b/OdinSerializer/Core/Formatters/ArrayFormatter.cs.meta index 14bcf37..0d74fc8 100644 --- a/OdinSerializer/Core/Formatters/ArrayFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/ArrayFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7579be6c3e5e041981ed3fec70c3c9d4 +guid: 0731e8f8288e84036bfd93438ee66620 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/ArrayListFormatter.cs.meta b/OdinSerializer/Core/Formatters/ArrayListFormatter.cs.meta index 8be8a9d..a87e055 100644 --- a/OdinSerializer/Core/Formatters/ArrayListFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/ArrayListFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 21eb72397bc1c92bbd23c6c597de783a +guid: 5750a13428f04b96e6f0c5c83d29fc47 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/BaseFormatter.cs b/OdinSerializer/Core/Formatters/BaseFormatter.cs index f006aa9..3b04544 100644 --- a/OdinSerializer/Core/Formatters/BaseFormatter.cs +++ b/OdinSerializer/Core/Formatters/BaseFormatter.cs @@ -373,6 +373,7 @@ protected void RegisterReferenceID(T value, IDataReader reader) /// The value to invoke the callbacks on. /// The deserialization context. [Obsolete("Use the InvokeOnDeserializingCallbacks variant that takes a ref T value instead. This is for struct compatibility reasons.", false)] + [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] protected void InvokeOnDeserializingCallbacks(T value, DeserializationContext context) { this.InvokeOnDeserializingCallbacks(ref value, context); @@ -414,4 +415,315 @@ protected void InvokeOnDeserializingCallbacks(ref T value, DeserializationContex /// The writer to serialize with. protected abstract void SerializeImplementation(ref T value, IDataWriter writer); } + + /// + /// Provides common functionality for serializing and deserializing weakly typed values of a given type, and provides automatic support for the following common serialization conventions: + /// + /// , , , , and . + /// + /// + public abstract class WeakBaseFormatter : IFormatter + { + protected delegate void SerializationCallback(object value, StreamingContext context); + protected readonly Type SerializedType; + + protected readonly SerializationCallback[] OnSerializingCallbacks; + protected readonly SerializationCallback[] OnSerializedCallbacks; + protected readonly SerializationCallback[] OnDeserializingCallbacks; + protected readonly SerializationCallback[] OnDeserializedCallbacks; + protected readonly bool IsValueType; + + protected readonly bool ImplementsISerializationCallbackReceiver; + protected readonly bool ImplementsIDeserializationCallback; + protected readonly bool ImplementsIObjectReference; + + Type IFormatter.SerializedType { get { return this.SerializedType; } } + + public WeakBaseFormatter(Type serializedType) + { + this.SerializedType = serializedType; + this.ImplementsISerializationCallbackReceiver = this.SerializedType.ImplementsOrInherits(typeof(UnityEngine.ISerializationCallbackReceiver)); + this.ImplementsIDeserializationCallback = this.SerializedType.ImplementsOrInherits(typeof(IDeserializationCallback)); + this.ImplementsIObjectReference = this.SerializedType.ImplementsOrInherits(typeof(IObjectReference)); + + if (this.SerializedType.ImplementsOrInherits(typeof(UnityEngine.Object))) + { + DefaultLoggers.DefaultLogger.LogWarning("A formatter has been created for the UnityEngine.Object type " + this.SerializedType.Name + " - this is *strongly* discouraged. Unity should be allowed to handle serialization and deserialization of its own weird objects. Remember to serialize with a UnityReferenceResolver as the external index reference resolver in the serialization context.\n\n Stacktrace: " + new System.Diagnostics.StackTrace().ToString()); + } + + MethodInfo[] methods = this.SerializedType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + + List callbacks = new List(); + + OnSerializingCallbacks = GetCallbacks(methods, typeof(OnSerializingAttribute), ref callbacks); + OnSerializedCallbacks = GetCallbacks(methods, typeof(OnSerializedAttribute), ref callbacks); + OnDeserializingCallbacks = GetCallbacks(methods, typeof(OnDeserializingAttribute), ref callbacks); + OnDeserializedCallbacks = GetCallbacks(methods, typeof(OnDeserializedAttribute), ref callbacks); + } + + private static SerializationCallback[] GetCallbacks(MethodInfo[] methods, Type callbackAttribute, ref List list) + { + for (int i = 0; i < methods.Length; i++) + { + var method = methods[i]; + + if (method.IsDefined(callbackAttribute, true)) + { + var callback = CreateCallback(method); + + if (callback != null) + { + list.Add(callback); + } + } + } + + var result = list.ToArray(); + list.Clear(); + return result; + } + + private static SerializationCallback CreateCallback(MethodInfo info) + { + var parameters = info.GetParameters(); + if (parameters.Length == 0) + { + return (object value, StreamingContext context) => + { + info.Invoke(value, null); + }; + } + else if (parameters.Length == 1 && parameters[0].ParameterType == typeof(StreamingContext) && parameters[0].ParameterType.IsByRef == false) + { + return (object value, StreamingContext context) => + { + info.Invoke(value, new object[] { context }); + }; + } + else + { + DefaultLoggers.DefaultLogger.LogWarning("The method " + info.GetNiceName() + " has an invalid signature and will be ignored by the serialization system."); + return null; + } + } + + /// + /// Serializes a value using a specified . + /// + /// The value to serialize. + /// The writer to use. + public void Serialize(object value, IDataWriter writer) + { + var context = writer.Context; + + for (int i = 0; i < OnSerializingCallbacks.Length; i++) + { + try + { + OnSerializingCallbacks[i](value, context.StreamingContext); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + + if (ImplementsISerializationCallbackReceiver) + { + try + { + + UnityEngine.ISerializationCallbackReceiver v = value as UnityEngine.ISerializationCallbackReceiver; + v.OnBeforeSerialize(); + value = v; + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + + try + { + this.SerializeImplementation(ref value, writer); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + + for (int i = 0; i < OnSerializedCallbacks.Length; i++) + { + try + { + OnSerializedCallbacks[i](value, context.StreamingContext); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + } + + /// + /// Deserializes a value using a specified . + /// + /// The reader to use. + /// + /// The deserialized value. + /// + public object Deserialize(IDataReader reader) + { + var context = reader.Context; + object value = this.GetUninitializedObject(); + + // We allow the above method to return null (for reference types) because of special cases like arrays, + // where the size of the array cannot be known yet, and thus we cannot create an object instance at this time. + // + // Therefore, those who override GetUninitializedObject and return null must call RegisterReferenceID and InvokeOnDeserializingCallbacks manually. + if (this.IsValueType) + { + if (object.ReferenceEquals(null, value)) + { + value = Activator.CreateInstance(this.SerializedType); + } + + this.InvokeOnDeserializingCallbacks(value, context); + } + else + { + if (object.ReferenceEquals(value, null) == false) + { + this.RegisterReferenceID(value, reader); + this.InvokeOnDeserializingCallbacks(value, context); + + if (ImplementsIObjectReference) + { + try + { + value = (value as IObjectReference).GetRealObject(context.StreamingContext); + this.RegisterReferenceID(value, reader); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + } + } + + try + { + this.DeserializeImplementation(ref value, reader); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + + // The deserialized value might be null, so check for that + if (this.IsValueType || object.ReferenceEquals(value, null) == false) + { + for (int i = 0; i < OnDeserializedCallbacks.Length; i++) + { + try + { + OnDeserializedCallbacks[i](value, context.StreamingContext); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + + if (ImplementsIDeserializationCallback) + { + IDeserializationCallback v = value as IDeserializationCallback; + v.OnDeserialization(this); + value = v; + } + + if (ImplementsISerializationCallbackReceiver) + { + try + { + UnityEngine.ISerializationCallbackReceiver v = value as UnityEngine.ISerializationCallbackReceiver; + v.OnAfterDeserialize(); + value = v; + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + } + + return value; + } + + /// + /// Registers the given object reference in the deserialization context. + /// + /// NOTE that this method only does anything if is not a value type. + /// + /// The value to register. + /// The reader which is currently being used. + protected void RegisterReferenceID(object value, IDataReader reader) + { + if (!this.IsValueType) + { + int id = reader.CurrentNodeId; + + if (id < 0) + { + reader.Context.Config.DebugContext.LogWarning("Reference type node is missing id upon deserialization. Some references may be broken. This tends to happen if a value type has changed to a reference type (IE, struct to class) since serialization took place."); + } + else + { + reader.Context.RegisterInternalReference(id, value); + } + } + } + + /// + /// Invokes all methods on the object with the [OnDeserializing] attribute. + /// + /// WARNING: This method will not be called automatically if you override GetUninitializedObject and return null! You will have to call it manually after having created the object instance during deserialization. + /// + /// The value to invoke the callbacks on. + /// The deserialization context. + protected void InvokeOnDeserializingCallbacks(object value, DeserializationContext context) + { + for (int i = 0; i < OnDeserializingCallbacks.Length; i++) + { + try + { + OnDeserializingCallbacks[i](value, context.StreamingContext); + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + } + } + } + + protected virtual object GetUninitializedObject() + { + return this.IsValueType ? Activator.CreateInstance(this.SerializedType) : FormatterServices.GetUninitializedObject(this.SerializedType); + } + + /// + /// Provides the actual implementation for deserializing a value of type . + /// + /// The uninitialized value to serialize into. This value will have been created earlier using . + /// The reader to deserialize with. + protected abstract void DeserializeImplementation(ref object value, IDataReader reader); + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected abstract void SerializeImplementation(ref object value, IDataWriter writer); + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/BaseFormatter.cs.meta b/OdinSerializer/Core/Formatters/BaseFormatter.cs.meta index 4a4f9e8..dfc6406 100644 --- a/OdinSerializer/Core/Formatters/BaseFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/BaseFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 843643a933fb7788b848a564e660f141 +guid: a10741a4e99c2fd5740b4d8ba1c08bd3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DateTimeFormatter.cs.meta b/OdinSerializer/Core/Formatters/DateTimeFormatter.cs.meta index c19cc64..8a81810 100644 --- a/OdinSerializer/Core/Formatters/DateTimeFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DateTimeFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d0c832787fff0ee5c7f8c3a482248838 +guid: 1380503d467e1d145ba6e5af1eb14cf6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DateTimeOffsetFormatter.cs.meta b/OdinSerializer/Core/Formatters/DateTimeOffsetFormatter.cs.meta index 0fb71bc..b5a42c7 100644 --- a/OdinSerializer/Core/Formatters/DateTimeOffsetFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DateTimeOffsetFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3f6c75575d579c34fafd715e2631e4a9 +guid: e3e5e94b4f2be2ea4ff8c7b4f22c4c42 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DelegateFormatter.cs b/OdinSerializer/Core/Formatters/DelegateFormatter.cs index 2de2ee1..5fb8613 100644 --- a/OdinSerializer/Core/Formatters/DelegateFormatter.cs +++ b/OdinSerializer/Core/Formatters/DelegateFormatter.cs @@ -30,20 +30,28 @@ namespace ToolBox.Serialization.OdinSerializer /// /// /// - public sealed class DelegateFormatter : BaseFormatter where T : class + public class DelegateFormatter : BaseFormatter where T : class { private static readonly Serializer ObjectSerializer = Serializer.Get(); private static readonly Serializer StringSerializer = Serializer.Get(); private static readonly Serializer TypeSerializer = Serializer.Get(); private static readonly Serializer TypeArraySerializer = Serializer.Get(); private static readonly Serializer DelegateArraySerializer = Serializer.Get(); + + public readonly Type DelegateType; - static DelegateFormatter() + public DelegateFormatter() : this(typeof(T)) { - if (typeof(Delegate).IsAssignableFrom(typeof(T)) == false) + } + + public DelegateFormatter(Type delegateType) + { + if (typeof(Delegate).IsAssignableFrom(delegateType) == false) { - throw new ArgumentException("The type " + typeof(T) + " is not a delegate."); + throw new ArgumentException("The type " + delegateType + " is not a delegate."); } + + this.DelegateType = delegateType; } /// @@ -56,7 +64,7 @@ protected override void DeserializeImplementation(ref T value, IDataReader reade string name; EntryType entry; - Type delegateType = typeof(T); + Type delegateType = this.DelegateType; Type declaringType = null; object target = null; string methodName = null; @@ -147,7 +155,7 @@ protected override void DeserializeImplementation(ref T value, IDataReader reade } catch (InvalidCastException) { - reader.Context.Config.DebugContext.LogWarning("Could not cast recombined delegate of type " + combinedDelegate.GetType().GetNiceFullName() + " to expected delegate type " + typeof(T).GetNiceFullName() + "."); + reader.Context.Config.DebugContext.LogWarning("Could not cast recombined delegate of type " + combinedDelegate.GetType().GetNiceFullName() + " to expected delegate type " + this.DelegateType.GetNiceFullName() + "."); } } @@ -379,4 +387,11 @@ protected override T GetUninitializedObject() return null; } } + + public class WeakDelegateFormatter : DelegateFormatter + { + public WeakDelegateFormatter(Type delegateType) : base(delegateType) + { + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/DelegateFormatter.cs.meta b/OdinSerializer/Core/Formatters/DelegateFormatter.cs.meta index 218096d..4240929 100644 --- a/OdinSerializer/Core/Formatters/DelegateFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DelegateFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: db83208b55399c954bc855c5da48b764 +guid: 1847ab02f426fdc3ee601a2a8026dd44 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs b/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs index 64fb772..512c48f 100644 --- a/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs +++ b/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs @@ -18,7 +18,7 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(DerivedDictionaryFormatter<,,>))] +[assembly: RegisterFormatter(typeof(DerivedDictionaryFormatter<,,>), weakFallback: typeof(WeakDictionaryFormatter), priority: -1)] namespace ToolBox.Serialization.OdinSerializer { diff --git a/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs.meta b/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs.meta index d574066..c036395 100644 --- a/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DerivedDictionaryFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8c8bcdabcf3f4a13368f9402ff81280d +guid: 3358dc2fbcff67e10a978da0b3ceadc7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DictionaryFormatter.cs b/OdinSerializer/Core/Formatters/DictionaryFormatter.cs index d2c7f28..96580cc 100644 --- a/OdinSerializer/Core/Formatters/DictionaryFormatter.cs +++ b/OdinSerializer/Core/Formatters/DictionaryFormatter.cs @@ -18,12 +18,15 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(DictionaryFormatter<,>))] +[assembly: RegisterFormatter(typeof(DictionaryFormatter<,>), weakFallback: typeof(WeakDictionaryFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; + using System.Collections; using System.Collections.Generic; + using System.Reflection; /// /// Custom generic formatter for the generic type definition . @@ -212,4 +215,219 @@ protected override void SerializeImplementation(ref Dictionary val } } } + + internal sealed class WeakDictionaryFormatter : WeakBaseFormatter + { + private readonly bool KeyIsValueType; + + private readonly Serializer EqualityComparerSerializer; + private readonly Serializer KeyReaderWriter; + private readonly Serializer ValueReaderWriter; + + private readonly ConstructorInfo ComparerConstructor; + private readonly PropertyInfo ComparerProperty; + private readonly PropertyInfo CountProperty; + private readonly Type KeyType; + private readonly Type ValueType; + + public WeakDictionaryFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetArgumentsOfInheritedOpenGenericClass(typeof(Dictionary<,>)); + + this.KeyType = args[0]; + this.ValueType = args[1]; + this.KeyIsValueType = this.KeyType.IsValueType; + this.KeyReaderWriter = Serializer.Get(this.KeyType); + this.ValueReaderWriter = Serializer.Get(this.ValueType); + + this.CountProperty = serializedType.GetProperty("Count"); + + if (this.CountProperty == null) + { + throw new SerializationAbortException("Can't serialize/deserialize the type " + serializedType.GetNiceFullName() + " because it has no accessible Count property."); + } + + try + { + // There's a very decent chance this type exists already and won't throw AOT-related exceptions + var equalityComparerType = typeof(IEqualityComparer<>).MakeGenericType(this.KeyType); + + this.EqualityComparerSerializer = Serializer.Get(equalityComparerType); + this.ComparerConstructor = serializedType.GetConstructor(new Type[] { equalityComparerType }); + this.ComparerProperty = serializedType.GetProperty("Comparer"); + } + catch (Exception) + { + // This is allowed to fail though, so just use fallbacks in that case + this.EqualityComparerSerializer = Serializer.Get(); + this.ComparerConstructor = null; + this.ComparerProperty = null; + } + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + object comparer = null; + + if (name == "comparer" || entry == EntryType.StartOfNode) + { + // There is a comparer serialized + comparer = EqualityComparerSerializer.ReadValueWeak(reader); + entry = reader.PeekEntry(out name); + } + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + Type type; + + if (!object.ReferenceEquals(comparer, null) && ComparerConstructor != null) + { + value = ComparerConstructor.Invoke(new object[] { comparer }); + } + else + { + value = Activator.CreateInstance(this.SerializedType); + } + + IDictionary dict = (IDictionary)value; + + // We must remember to register the dictionary reference ourselves, since we returned null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + // There aren't any OnDeserializing callbacks on dictionaries that we're interested in. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + bool exitNode = true; + + try + { + reader.EnterNode(out type); + object key = KeyReaderWriter.ReadValueWeak(reader); + object val = ValueReaderWriter.ReadValueWeak(reader); + + if (!KeyIsValueType && object.ReferenceEquals(key, null)) + { + reader.Context.Config.DebugContext.LogWarning("Dictionary key of type '" + this.KeyType.FullName + "' was null upon deserialization. A key has gone missing."); + continue; + } + + dict[key] = val; + } + catch (SerializationAbortException ex) + { + exitNode = false; + throw ex; + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + finally + { + if (exitNode) + { + reader.ExitNode(); + } + } + + if (reader.IsInArrayNode == false) + { + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + IDictionary dict = (IDictionary)value; + + if (this.ComparerProperty != null) + { + object comparer = this.ComparerProperty.GetValue(value, null); + + if (!object.ReferenceEquals(comparer, null)) + { + EqualityComparerSerializer.WriteValueWeak("comparer", comparer, writer); + } + } + + writer.BeginArrayNode((int)this.CountProperty.GetValue(value, null)); + + var enumerator = dict.GetEnumerator(); + + try + { + while (enumerator.MoveNext()) + { + bool endNode = true; + + try + { + writer.BeginStructNode(null, null); + KeyReaderWriter.WriteValueWeak("$k", enumerator.Key, writer); + ValueReaderWriter.WriteValueWeak("$v", enumerator.Value, writer); + } + catch (SerializationAbortException ex) + { + endNode = false; + throw ex; + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + finally + { + if (endNode) + { + writer.EndNode(null); + } + } + } + } + finally + { + enumerator.Reset(); + IDisposable dispose = enumerator as IDisposable; + if (dispose != null) dispose.Dispose(); + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/DictionaryFormatter.cs.meta b/OdinSerializer/Core/Formatters/DictionaryFormatter.cs.meta index d165337..f5cd5c4 100644 --- a/OdinSerializer/Core/Formatters/DictionaryFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DictionaryFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0d7f249b9cc2f220eebcd1abdb9f1934 +guid: c3d11ce07047ca0d818a4324996f0740 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs b/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs index b1805dc..e11553f 100644 --- a/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs +++ b/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs @@ -18,12 +18,13 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(DoubleLookupDictionaryFormatter<,,>))] +[assembly: RegisterFormatter(typeof(DoubleLookupDictionaryFormatter<,,>), weakFallback: typeof(WeakDoubleLookupDictionaryFormatter))] namespace ToolBox.Serialization.OdinSerializer { using ToolBox.Serialization.OdinSerializer.Utilities; using System; + using System.Collections; using System.Collections.Generic; /// @@ -176,4 +177,144 @@ protected override void DeserializeImplementation(ref DoubleLookupDictionary)); + + this.PrimaryReaderWriter = Serializer.Get(args[0]); + this.InnerReaderWriter = Serializer.Get(args[1]); + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + var dict = (IDictionary)value; + writer.BeginArrayNode(dict.Count); + + bool endNode = true; + var enumerator = dict.GetEnumerator(); + + try + { + while (enumerator.MoveNext()) + { + try + { + writer.BeginStructNode(null, null); + PrimaryReaderWriter.WriteValueWeak("$k", enumerator.Key, writer); + InnerReaderWriter.WriteValueWeak("$v", enumerator.Value, writer); + } + catch (SerializationAbortException ex) + { + endNode = false; + throw ex; + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + finally + { + if (endNode) + { + writer.EndNode(null); + } + } + } + } + finally + { + enumerator.Reset(); + IDisposable dispose = enumerator as IDisposable; + if (dispose != null) dispose.Dispose(); + } + } + finally + { + writer.EndArrayNode(); + } + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + Type type; + value = Activator.CreateInstance(this.SerializedType); + var dict = (IDictionary)value; + + this.RegisterReferenceID(value, reader); + + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + bool exitNode = true; + + try + { + reader.EnterNode(out type); + object key = PrimaryReaderWriter.ReadValueWeak(reader); + object inner = InnerReaderWriter.ReadValueWeak(reader); + + dict.Add(key, inner); + } + catch (SerializationAbortException ex) + { + exitNode = false; + throw ex; + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + finally + { + if (exitNode) + { + reader.ExitNode(); + } + } + + if (reader.IsInArrayNode == false) + { + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs.meta b/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs.meta index 5b79128..a7cea74 100644 --- a/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/DoubleLookupDictionaryFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5f3266377a7b19d23d03a96e2dfee65d +guid: 1034d60868c2ef60aae2bcadef2304ee MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/EasyBaseFormatter.cs.meta b/OdinSerializer/Core/Formatters/EasyBaseFormatter.cs.meta index 8c550a8..dc0b016 100644 --- a/OdinSerializer/Core/Formatters/EasyBaseFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/EasyBaseFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3b04eeb684fad203bad2b09aaa0980d5 +guid: 739f7f7011be534a842dde18fd104134 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/EmittedFormatterAttribute.cs.meta b/OdinSerializer/Core/Formatters/EmittedFormatterAttribute.cs.meta index 68c1721..a42ff2b 100644 --- a/OdinSerializer/Core/Formatters/EmittedFormatterAttribute.cs.meta +++ b/OdinSerializer/Core/Formatters/EmittedFormatterAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4dd6ec9367601d43de228e64a8181ea1 +guid: 9089928d003280e6e43fcdc85313245e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/EmptyTypeFormatter.cs.meta b/OdinSerializer/Core/Formatters/EmptyTypeFormatter.cs.meta index 80d853f..ad34099 100644 --- a/OdinSerializer/Core/Formatters/EmptyTypeFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/EmptyTypeFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 04adfe3ed35c83ca1628cf63222458da +guid: 443ba0beaf4ddb6d7974b709dd26181a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/FormatterEmitter.cs.meta b/OdinSerializer/Core/Formatters/FormatterEmitter.cs.meta index 6c560f3..b7aa45a 100644 --- a/OdinSerializer/Core/Formatters/FormatterEmitter.cs.meta +++ b/OdinSerializer/Core/Formatters/FormatterEmitter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 68d197d9127cb37b114dd420c8e2dbb9 +guid: 2698c093decbf4fa2b3f11244cbcb8dc MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs b/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs index c5b8abd..e156f93 100644 --- a/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs +++ b/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs @@ -19,7 +19,9 @@ namespace ToolBox.Serialization.OdinSerializer { using ToolBox.Serialization.OdinSerializer.Utilities; using System; + using System.Collections; using System.Collections.Generic; + using System.Reflection; /// /// Utility class for the class. @@ -180,4 +182,133 @@ protected override void SerializeImplementation(ref TCollection value, IDataWrit } } } + + public sealed class WeakGenericCollectionFormatter : WeakBaseFormatter + { + private readonly Serializer ValueReaderWriter; + private readonly Type ElementType; + private readonly PropertyInfo CountProperty; + private readonly MethodInfo AddMethod; + + public WeakGenericCollectionFormatter(Type collectionType, Type elementType) : base(collectionType) + { + this.ElementType = elementType; + this.CountProperty = collectionType.GetProperty("Count", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); + this.AddMethod = collectionType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { elementType }, null); + + if (this.AddMethod == null) + { + throw new ArgumentException("Cannot treat the type " + collectionType.Name + " as a generic collection since it has no accessible Add method."); + } + + if (this.CountProperty == null || this.CountProperty.PropertyType != typeof(int)) + { + throw new ArgumentException("Cannot treat the type " + collectionType.Name + " as a generic collection since it has no accessible Count property."); + } + + Type e; + + if (GenericCollectionFormatter.CanFormat(collectionType, out e) == false) + { + throw new ArgumentException("Cannot treat the type " + collectionType.Name + " as a generic collection."); + } + + if (e != elementType) + { + throw new ArgumentException("Type " + elementType.Name + " is not the element type of the generic collection type " + collectionType.Name + "."); + } + } + + /// + /// Gets a new object of type . + /// + /// + /// A new object of type . + /// + protected override object GetUninitializedObject() + { + return Activator.CreateInstance(this.SerializedType); + } + + /// + /// Provides the actual implementation for deserializing a value of type . + /// + /// The uninitialized value to serialize into. This value will have been created earlier using . + /// The reader to deserialize with. + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + var addParams = new object[1]; + + try + { + addParams[0] = ValueReaderWriter.ReadValueWeak(reader); + AddMethod.Invoke(value, addParams); + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + + if (reader.IsInArrayNode == false) + { + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + writer.BeginArrayNode((int)CountProperty.GetValue(value, null)); + + foreach (var element in (IEnumerable)value) + { + ValueReaderWriter.WriteValueWeak(element, writer); + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs.meta b/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs.meta index 73af06e..7415727 100644 --- a/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/GenericCollectionFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 707c24528047cacbe13ad82d1bef1954 +guid: 9603f33d0f833c8a0bd33b80a8be6224 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/HashSetFormatter.cs b/OdinSerializer/Core/Formatters/HashSetFormatter.cs index 267f511..ee2e606 100644 --- a/OdinSerializer/Core/Formatters/HashSetFormatter.cs +++ b/OdinSerializer/Core/Formatters/HashSetFormatter.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// +// // Copyright (c) 2018 Sirenix IVS // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,12 +18,15 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(HashSetFormatter<>))] +[assembly: RegisterFormatter(typeof(HashSetFormatter<>), weakFallback: typeof(WeakHashSetFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; + using System.Collections; using System.Collections.Generic; + using System.Reflection; /// /// Custom generic formatter for the generic type definition . @@ -138,4 +141,125 @@ protected override void SerializeImplementation(ref HashSet value, IDataWrite } } } + + public class WeakHashSetFormatter : WeakBaseFormatter + { + private readonly Serializer ElementSerializer; + private readonly MethodInfo AddMethod; + private readonly PropertyInfo CountProperty; + + public WeakHashSetFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetArgumentsOfInheritedOpenGenericClass(typeof(HashSet<>)); + this.ElementSerializer = Serializer.Get(args[0]); + + this.AddMethod = serializedType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { args[0] }, null); + this.CountProperty = serializedType.GetProperty("Count", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); + + if (this.AddMethod == null) + { + throw new SerializationAbortException("Can't serialize/deserialize hashset of type '" + serializedType.GetNiceFullName() + "' since a proper Add method wasn't found."); + } + + if (this.CountProperty == null) + { + throw new SerializationAbortException("Can't serialize/deserialize hashset of type '" + serializedType.GetNiceFullName() + "' since a proper Count property wasn't found."); + } + } + + /// + /// Returns null. + /// + /// + /// A null value. + /// + protected override object GetUninitializedObject() + { + return null; + } + + /// + /// Provides the actual implementation for deserializing a value of type . + /// + /// The uninitialized value to serialize into. This value will have been created earlier using . + /// The reader to deserialize with. + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + value = Activator.CreateInstance(this.SerializedType); + + // We must remember to register the hashset reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + var addParams = new object[1]; + + // There aren't any relevant OnDeserializing callbacks on hash sets. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + addParams[0] = ElementSerializer.ReadValueWeak(reader); + this.AddMethod.Invoke(value, addParams); + + if (reader.IsInArrayNode == false) + { + // Something has gone wrong + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + writer.BeginArrayNode((int)this.CountProperty.GetValue(value, null)); + + foreach (object item in ((IEnumerable)value)) + { + try + { + ElementSerializer.WriteValueWeak(item, writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/HashSetFormatter.cs.meta b/OdinSerializer/Core/Formatters/HashSetFormatter.cs.meta index 07842c2..8ed3ccc 100644 --- a/OdinSerializer/Core/Formatters/HashSetFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/HashSetFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c5661456c5c273b6ffc40538f8950baa +guid: f9acc43b01632dcdfd311c735dfdab4f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/IFormatter.cs.meta b/OdinSerializer/Core/Formatters/IFormatter.cs.meta index 53580d2..60faf3a 100644 --- a/OdinSerializer/Core/Formatters/IFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/IFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e2c4e2d93ba7f66267b859de1289b1b8 +guid: aa4d6f6900bd51dce01be0cb95ad9036 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs b/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs index e707fec..d0dfb8d 100644 --- a/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs +++ b/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs @@ -18,11 +18,13 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(KeyValuePairFormatter<,>))] +[assembly: RegisterFormatter(typeof(KeyValuePairFormatter<,>), weakFallback: typeof(WeakKeyValuePairFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using System; using System.Collections.Generic; + using System.Reflection; /// /// Custom generic formatter for the generic type definition . @@ -60,4 +62,38 @@ protected override void DeserializeImplementation(ref KeyValuePair ); } } + + public sealed class WeakKeyValuePairFormatter : WeakBaseFormatter + { + private readonly Serializer KeySerializer; + private readonly Serializer ValueSerializer; + + private readonly PropertyInfo KeyProperty; + private readonly PropertyInfo ValueProperty; + + public WeakKeyValuePairFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetGenericArguments(); + + this.KeySerializer = Serializer.Get(args[0]); + this.ValueSerializer = Serializer.Get(args[1]); + + this.KeyProperty = serializedType.GetProperty("Key"); + this.ValueProperty = serializedType.GetProperty("Value"); + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + KeySerializer.WriteValueWeak(KeyProperty.GetValue(value, null), writer); + ValueSerializer.WriteValueWeak(ValueProperty.GetValue(value, null), writer); + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + value = Activator.CreateInstance(this.SerializedType, + KeySerializer.ReadValueWeak(reader), + ValueSerializer.ReadValueWeak(reader) + ); + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs.meta b/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs.meta index 2de37b4..8cd9863 100644 --- a/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/KeyValuePairFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9a203d5eb2a62f6c6c0885c1ce493686 +guid: bf765f69604b743ff733d5ceb9402ad1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/ListFormatter.cs b/OdinSerializer/Core/Formatters/ListFormatter.cs index 304f59a..ba306c9 100644 --- a/OdinSerializer/Core/Formatters/ListFormatter.cs +++ b/OdinSerializer/Core/Formatters/ListFormatter.cs @@ -18,11 +18,13 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(ListFormatter<>))] +[assembly: RegisterFormatter(typeof(ListFormatter<>), weakFallback: typeof(WeakListFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; + using System.Collections; using System.Collections.Generic; /// @@ -138,4 +140,93 @@ protected override void SerializeImplementation(ref List value, IDataWriter w } } } + + public class WeakListFormatter : WeakBaseFormatter + { + private readonly Serializer ElementSerializer; + + public WeakListFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetArgumentsOfInheritedOpenGenericClass(typeof(List<>)); + this.ElementSerializer = Serializer.Get(args[0]); + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + value = Activator.CreateInstance(this.SerializedType, (int)length); + IList list = (IList)value; + + // We must remember to register the list reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + // There aren't any OnDeserializing callbacks on lists. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + list.Add(ElementSerializer.ReadValueWeak(reader)); + + if (reader.IsInArrayNode == false) + { + // Something has gone wrong + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + IList list = (IList)value; + writer.BeginArrayNode(list.Count); + + for (int i = 0; i < list.Count; i++) + { + try + { + ElementSerializer.WriteValueWeak(list[i], writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/ListFormatter.cs.meta b/OdinSerializer/Core/Formatters/ListFormatter.cs.meta index c4bbf44..94f6011 100644 --- a/OdinSerializer/Core/Formatters/ListFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/ListFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 56b84374ad14e323f8e90945275896d5 +guid: 1b09a38475c1317cc0604031a20ec8c2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs b/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs index 07afed9..d8142da 100644 --- a/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs +++ b/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs @@ -18,7 +18,7 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(MethodInfoFormatter<>))] +[assembly: RegisterFormatter(typeof(MethodInfoFormatter<>), weakFallback: typeof(WeakMethodInfoFormatter))] namespace ToolBox.Serialization.OdinSerializer { @@ -31,7 +31,7 @@ namespace ToolBox.Serialization.OdinSerializer /// Custom formatter for MethodInfo, since Unity Mono's MethodInfo ISerializable implementation will often crash if the method no longer exists upon deserialization. /// /// - public sealed class MethodInfoFormatter : BaseFormatter + public class MethodInfoFormatter : BaseFormatter where T : MethodInfo { private static readonly Serializer StringSerializer = Serializer.Get(); @@ -55,21 +55,9 @@ protected override void DeserializeImplementation(ref T value, IDataReader reade // We have legacy ISerializable data for the MethodInfo, since in no case will data written by this formatter ever start with an array. // In this case, get the proper legacy formatter for this type and read the data using that. - IFormatter serializableFormatter; - - try - { - serializableFormatter = (IFormatter)Activator.CreateInstance(typeof(SerializableFormatter<>).MakeGenericType(typeof(T))); - } - catch (Exception) - { - reader.Context.Config.DebugContext.LogWarning("MethodInfo with legacy ISerializable data serialized was read in a context where a SerializableFormatter formatter for the type could not be instantiated, likely in an IL2CPP build. This means legacy data cannot be read properly - please reserialize all data in your project to ensure no legacy MethodInfo data is included in your build, as this case is not AOT-supported by default."); - - value = null; - return; - } - - value = serializableFormatter.Deserialize(reader); + IFormatter serializableFormatter; + serializableFormatter = new WeakSerializableFormatter(typeof(T)); + value = (T)(object)serializableFormatter.Deserialize(reader); return; } @@ -298,4 +286,8 @@ protected override T GetUninitializedObject() return null; } } + + public class WeakMethodInfoFormatter : MethodInfoFormatter + { + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs.meta b/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs.meta index 1b68b69..93275e7 100644 --- a/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/MethodInfoFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8283dd601ab97d40aeda804edbaa8216 +guid: a71303cdd37d60246468a972a816378c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs b/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs index 54295b0..69d5654 100644 --- a/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs +++ b/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs @@ -156,4 +156,106 @@ protected void RegisterReferenceID(T value, IDataReader reader) } } } + + public abstract class WeakMinimalBaseFormatter : IFormatter + { + protected readonly Type SerializedType; + + /// + /// Whether the serialized value is a value type. + /// + protected readonly bool IsValueType; + + /// + /// Gets the type that the formatter can serialize. + /// + /// + /// The type that the formatter can serialize. + /// + Type IFormatter.SerializedType { get { return this.SerializedType; } } + + public WeakMinimalBaseFormatter(Type serializedType) + { + this.SerializedType = serializedType; + this.IsValueType = this.SerializedType.IsValueType; + } + + public object Deserialize(IDataReader reader) + { + object result = this.GetUninitializedObject(); + + // We allow the above method to return null (for reference types) because of special cases like arrays, + // where the size of the array cannot be known yet, and thus we cannot create an object instance at this time. + // + // Therefore, those who override GetUninitializedObject and return null must call RegisterReferenceID manually. + if (this.IsValueType == false && object.ReferenceEquals(result, null) == false) + { + this.RegisterReferenceID(result, reader); + } + + this.Read(ref result, reader); + return result; + } + + public void Serialize(object value, IDataWriter writer) + { + this.Write(ref value, writer); + } + + /// + /// Get an uninitialized object of type . WARNING: If you override this and return null, the object's ID will not be automatically registered. + /// You will have to call immediately after creating the object yourself during deserialization. + /// + /// An uninitialized object of type . + protected virtual object GetUninitializedObject() + { + if (this.IsValueType) + { + return Activator.CreateInstance(this.SerializedType); + } + else + { + return FormatterServices.GetUninitializedObject(this.SerializedType); + } + } + + /// + /// Reads into the specified value using the specified reader. + /// + /// The value to read into. + /// The reader to use. + protected abstract void Read(ref object value, IDataReader reader); + + /// + /// Writes from the specified value using the specified writer. + /// + /// The value to write from. + /// The writer to use. + protected abstract void Write(ref object value, IDataWriter writer); + + /// + /// Registers the given object reference in the deserialization context. + /// + /// NOTE that this method only does anything if the serialized type is not a value type. + /// + /// The value to register. + /// The reader which is currently being used. + protected void RegisterReferenceID(object value, IDataReader reader) + { + if (!this.IsValueType) + { + // Get ID and register object reference + int id = reader.CurrentNodeId; + + if (id < 0) + { + reader.Context.Config.DebugContext.LogWarning("Reference type node is missing id upon deserialization. Some references may be broken. This tends to happen if a value type has changed to a reference type (IE, struct to class) since serialization took place."); + } + else + { + reader.Context.RegisterInternalReference(id, value); + } + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs.meta b/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs.meta index 178284d..3c8fdae 100644 --- a/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/MinimalBaseFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9a1f0db00e4dac731d8f659a89c5a396 +guid: 96e935eac4129a1dc69906d86939c033 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs b/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs index 0d77db2..672ea29 100644 --- a/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs +++ b/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs @@ -280,4 +280,244 @@ private void IterateArrayRead(Array a, int rank, int[] indices, Action } } } + + public sealed class WeakMultiDimensionalArrayFormatter : WeakBaseFormatter + { + private const string RANKS_NAME = "ranks"; + private const char RANKS_SEPARATOR = '|'; + + private readonly int ArrayRank; + private readonly Type ElementType; + private readonly Serializer ValueReaderWriter; + + public WeakMultiDimensionalArrayFormatter(Type arrayType, Type elementType) : base(arrayType) + { + this.ArrayRank = arrayType.GetArrayRank(); + this.ElementType = elementType; + this.ValueReaderWriter = Serializer.Get(elementType); + } + + /// + /// Returns null. + /// + /// + /// A null value. + /// + protected override object GetUninitializedObject() + { + return null; + } + + /// + /// Provides the actual implementation for deserializing a value of type . + /// + /// The uninitialized value to serialize into. This value will have been created earlier using . + /// The reader to deserialize with. + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + long length; + reader.EnterArray(out length); + + entry = reader.PeekEntry(out name); + + if (entry != EntryType.String || name != RANKS_NAME) + { + value = null; + reader.SkipEntry(); + return; + } + + string lengthStr; + reader.ReadString(out lengthStr); + + string[] lengthsStrs = lengthStr.Split(RANKS_SEPARATOR); + + if (lengthsStrs.Length != ArrayRank) + { + value = null; + reader.SkipEntry(); + return; + } + + int[] lengths = new int[lengthsStrs.Length]; + + for (int i = 0; i < lengthsStrs.Length; i++) + { + int rankVal; + if (int.TryParse(lengthsStrs[i], out rankVal)) + { + lengths[i] = rankVal; + } + else + { + value = null; + reader.SkipEntry(); + return; + } + } + + long rankTotal = lengths[0]; + + for (int i = 1; i < lengths.Length; i++) + { + rankTotal *= lengths[i]; + } + + if (rankTotal != length) + { + value = null; + reader.SkipEntry(); + return; + } + + value = Array.CreateInstance(this.ElementType, lengths); + + // We must remember to register the array reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + // There aren't any OnDeserializing callbacks on arrays. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + int elements = 0; + + try + { + this.IterateArrayWrite( + (Array)(object)value, + () => + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + elements + " elements, when " + length + " elements were expected."); + throw new InvalidOperationException(); + } + + var v = ValueReaderWriter.ReadValueWeak(reader); + + if (reader.IsInArrayNode == false) + { + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + throw new InvalidOperationException(); + } + + elements++; + return v; + }); + } + catch (InvalidOperationException) + { + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + + reader.ExitArray(); + } + else + { + value = null; + reader.SkipEntry(); + } + } + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + var array = value as Array; + + try + { + writer.BeginArrayNode(array.LongLength); + + int[] lengths = new int[ArrayRank]; + + for (int i = 0; i < ArrayRank; i++) + { + lengths[i] = array.GetLength(i); + } + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < ArrayRank; i++) + { + if (i > 0) + { + sb.Append(RANKS_SEPARATOR); + } + + sb.Append(lengths[i].ToString(CultureInfo.InvariantCulture)); + } + + string lengthStr = sb.ToString(); + + writer.WriteString(RANKS_NAME, lengthStr); + + this.IterateArrayRead( + (Array)(object)value, + (v) => + { + ValueReaderWriter.WriteValueWeak(v, writer); + }); + } + finally + { + writer.EndArrayNode(); + } + } + + private void IterateArrayWrite(Array a, Func write) + { + int[] indices = new int[ArrayRank]; + this.IterateArrayWrite(a, 0, indices, write); + } + + private void IterateArrayWrite(Array a, int rank, int[] indices, Func write) + { + for (int i = 0; i < a.GetLength(rank); i++) + { + indices[rank] = i; + + if (rank + 1 < a.Rank) + { + this.IterateArrayWrite(a, rank + 1, indices, write); + } + else + { + a.SetValue(write(), indices); + } + } + } + + private void IterateArrayRead(Array a, Action read) + { + int[] indices = new int[ArrayRank]; + this.IterateArrayRead(a, 0, indices, read); + } + + private void IterateArrayRead(Array a, int rank, int[] indices, Action read) + { + for (int i = 0; i < a.GetLength(rank); i++) + { + indices[rank] = i; + + if (rank + 1 < a.Rank) + { + this.IterateArrayRead(a, rank + 1, indices, read); + } + else + { + read(a.GetValue(indices)); + } + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs.meta b/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs.meta index de83ff2..24ab444 100644 --- a/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/MultiDimensionalArrayFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e9c668542a44ad6d30f3395201fcb90f +guid: a38886156778da161695ee94ef232782 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/NullableFormatter.cs b/OdinSerializer/Core/Formatters/NullableFormatter.cs index 1b8cb1a..5715aa7 100644 --- a/OdinSerializer/Core/Formatters/NullableFormatter.cs +++ b/OdinSerializer/Core/Formatters/NullableFormatter.cs @@ -18,7 +18,7 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(NullableFormatter<>))] +[assembly: RegisterFormatter(typeof(NullableFormatter<>), weakFallback: typeof(WeakNullableFormatter))] namespace ToolBox.Serialization.OdinSerializer { @@ -86,4 +86,48 @@ protected override void SerializeImplementation(ref T? value, IDataWriter writer } } } + + public sealed class WeakNullableFormatter : WeakBaseFormatter + { + private readonly Serializer ValueSerializer; + + public WeakNullableFormatter(Type nullableType) : base(nullableType) + { + var args = nullableType.GetGenericArguments(); + this.ValueSerializer = Serializer.Get(args[0]); + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.Null) + { + value = null; + reader.ReadNull(); + } + else + { + value = this.ValueSerializer.ReadValueWeak(reader); + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + if (value != null) + { + this.ValueSerializer.WriteValueWeak(value, writer); + } + else + { + writer.WriteNull(null); + } + } + + protected override object GetUninitializedObject() + { + return null; + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/NullableFormatter.cs.meta b/OdinSerializer/Core/Formatters/NullableFormatter.cs.meta index f5a3fb3..374e0f0 100644 --- a/OdinSerializer/Core/Formatters/NullableFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/NullableFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f234a6f5ae8faeb4fbb42314910cfb86 +guid: 5974cc3e760264b1a4b6e8da69bcfcb7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs b/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs index b8c3417..ae63cd7 100644 --- a/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs +++ b/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs @@ -16,8 +16,13 @@ // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; + using System; + using System.Collections.Generic; + /// /// Formatter for all primitive one-dimensional arrays. /// @@ -50,6 +55,10 @@ protected override void Read(ref T[] value, IDataReader reader) reader.ReadPrimitiveArray(out value); this.RegisterReferenceID(value, reader); } + else + { + reader.SkipEntry(); + } } /// @@ -62,4 +71,216 @@ protected override void Write(ref T[] value, IDataWriter writer) writer.WritePrimitiveArray(value); } } + + public sealed class WeakPrimitiveArrayFormatter : WeakMinimalBaseFormatter + { + private static readonly Dictionary PrimitiveTypes = new Dictionary(FastTypeComparer.Instance) + { + { typeof(char ), PrimitiveArrayType.PrimitiveArray_char }, + { typeof(sbyte ), PrimitiveArrayType.PrimitiveArray_sbyte }, + { typeof(short ), PrimitiveArrayType.PrimitiveArray_short }, + { typeof(int ), PrimitiveArrayType.PrimitiveArray_int }, + { typeof(long ), PrimitiveArrayType.PrimitiveArray_long }, + { typeof(byte ), PrimitiveArrayType.PrimitiveArray_byte }, + { typeof(ushort ), PrimitiveArrayType.PrimitiveArray_ushort }, + { typeof(uint ), PrimitiveArrayType.PrimitiveArray_uint }, + { typeof(ulong ), PrimitiveArrayType.PrimitiveArray_ulong }, + { typeof(decimal), PrimitiveArrayType.PrimitiveArray_decimal }, + { typeof(bool ), PrimitiveArrayType.PrimitiveArray_bool }, + { typeof(float ), PrimitiveArrayType.PrimitiveArray_float }, + { typeof(double ), PrimitiveArrayType.PrimitiveArray_double }, + { typeof(Guid ), PrimitiveArrayType.PrimitiveArray_Guid }, + }; + + public enum PrimitiveArrayType + { + PrimitiveArray_char , + PrimitiveArray_sbyte , + PrimitiveArray_short , + PrimitiveArray_int , + PrimitiveArray_long , + PrimitiveArray_byte , + PrimitiveArray_ushort , + PrimitiveArray_uint , + PrimitiveArray_ulong , + PrimitiveArray_decimal , + PrimitiveArray_bool , + PrimitiveArray_float , + PrimitiveArray_double , + PrimitiveArray_Guid , + } + + private readonly Type ElementType; + private readonly PrimitiveArrayType PrimitiveType; + + public WeakPrimitiveArrayFormatter(Type arrayType, Type elementType) : base(arrayType) + { + this.ElementType = elementType; + + if (!PrimitiveTypes.TryGetValue(elementType, out this.PrimitiveType)) + { + throw new SerializationAbortException("The type '" + elementType.GetNiceFullName() + "' is not a type that can be written as a primitive array, yet the primitive array formatter is being used for it."); + } + } + + /// + /// Returns null. + /// + /// + /// A null value. + /// + protected override object GetUninitializedObject() + { + return null; + } + + /// + /// Reads into the specified value using the specified reader. + /// + /// The value to read into. + /// The reader to use. + protected override void Read(ref object value, IDataReader reader) + { + string name; + + if (reader.PeekEntry(out name) == EntryType.PrimitiveArray) + { + switch (this.PrimitiveType) + { + case PrimitiveArrayType.PrimitiveArray_char: + { + char[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_sbyte: + { + sbyte[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_short: + { + short[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_int: + { + int[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_long: + { + long[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_byte: + { + byte[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_ushort: + { + ushort[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_uint: + { + uint[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_ulong: + { + ulong[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_decimal: + { + decimal[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_bool: + { + bool[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_float: + { + float[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_double: + { + double[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + case PrimitiveArrayType.PrimitiveArray_Guid: + { + Guid[] readValue; + reader.ReadPrimitiveArray(out readValue); + value = readValue; + } + break; + default: + throw new NotImplementedException(); + } + + this.RegisterReferenceID(value, reader); + } + else + { + reader.SkipEntry(); + } + } + + /// + /// Writes from the specified value using the specified writer. + /// + /// The value to write from. + /// The writer to use. + protected override void Write(ref object value, IDataWriter writer) + { + switch (this.PrimitiveType) + { + case PrimitiveArrayType.PrimitiveArray_char : writer.WritePrimitiveArray((char [])value); break; + case PrimitiveArrayType.PrimitiveArray_sbyte : writer.WritePrimitiveArray((sbyte [])value); break; + case PrimitiveArrayType.PrimitiveArray_short : writer.WritePrimitiveArray((short [])value); break; + case PrimitiveArrayType.PrimitiveArray_int : writer.WritePrimitiveArray((int [])value); break; + case PrimitiveArrayType.PrimitiveArray_long : writer.WritePrimitiveArray((long [])value); break; + case PrimitiveArrayType.PrimitiveArray_byte : writer.WritePrimitiveArray((byte [])value); break; + case PrimitiveArrayType.PrimitiveArray_ushort : writer.WritePrimitiveArray((ushort [])value); break; + case PrimitiveArrayType.PrimitiveArray_uint : writer.WritePrimitiveArray((uint [])value); break; + case PrimitiveArrayType.PrimitiveArray_ulong : writer.WritePrimitiveArray((ulong [])value); break; + case PrimitiveArrayType.PrimitiveArray_decimal: writer.WritePrimitiveArray((decimal[])value); break; + case PrimitiveArrayType.PrimitiveArray_bool : writer.WritePrimitiveArray((bool [])value); break; + case PrimitiveArrayType.PrimitiveArray_float : writer.WritePrimitiveArray((float [])value); break; + case PrimitiveArrayType.PrimitiveArray_double : writer.WritePrimitiveArray((double [])value); break; + case PrimitiveArrayType.PrimitiveArray_Guid : writer.WritePrimitiveArray((Guid [])value); break; + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs.meta b/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs.meta index b99cd0e..8a13540 100644 --- a/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/PrimitiveArrayFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b6b3193e39edcbcf1ec5710ceca4e564 +guid: 318aea443cbe7bef382e506c0facbdc5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/QueueFormatter.cs b/OdinSerializer/Core/Formatters/QueueFormatter.cs index 09346b3..0ef40a5 100644 --- a/OdinSerializer/Core/Formatters/QueueFormatter.cs +++ b/OdinSerializer/Core/Formatters/QueueFormatter.cs @@ -18,12 +18,15 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(QueueFormatter<,>))] +[assembly: RegisterFormatter(typeof(QueueFormatter<,>), weakFallback: typeof(WeakQueueFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; + using System.Collections; using System.Collections.Generic; + using System.Reflection; /// /// Custom generic formatter for the generic type definition . @@ -148,4 +151,115 @@ protected override void SerializeImplementation(ref TQueue value, IDataWriter wr } } } + + public class WeakQueueFormatter : WeakBaseFormatter + { + private readonly Serializer ElementSerializer; + private readonly bool IsPlainQueue; + private MethodInfo EnqueueMethod; + + public WeakQueueFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetArgumentsOfInheritedOpenGenericClass(typeof(Queue<>)); + this.ElementSerializer = Serializer.Get(args[0]); + this.IsPlainQueue = serializedType.IsGenericType && serializedType.GetGenericTypeDefinition() == typeof(Queue<>); + this.EnqueueMethod = serializedType.GetMethod("Enqueue", BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic, null, new Type[] { args[0] }, null); + + if (this.EnqueueMethod == null) + { + throw new SerializationAbortException("Can't serialize type '" + serializedType.GetNiceFullName() + "' because no proper Enqueue method was found."); + } + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + + if (IsPlainQueue) + { + value = Activator.CreateInstance(this.SerializedType, (int)length); + } + else + { + value = Activator.CreateInstance(this.SerializedType); + } + + var collection = (ICollection)value; + + // We must remember to register the queue reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + var enqueueParams = new object[1]; + + // There aren't any OnDeserializing callbacks on queues. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + enqueueParams[0] = this.ElementSerializer.ReadValueWeak(reader); + EnqueueMethod.Invoke(value, enqueueParams); + + if (reader.IsInArrayNode == false) + { + // Something has gone wrong + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + var collection = (ICollection)value; + + writer.BeginArrayNode(collection.Count); + + foreach (var element in collection) + { + try + { + this.ElementSerializer.WriteValueWeak(element, writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/QueueFormatter.cs.meta b/OdinSerializer/Core/Formatters/QueueFormatter.cs.meta index 377cad3..1b44568 100644 --- a/OdinSerializer/Core/Formatters/QueueFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/QueueFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 56d552b54540cbfd3d8ef90a231511c1 +guid: 0222238181c77e6e4f7ec583dde526c3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/ReflectionFormatter.cs b/OdinSerializer/Core/Formatters/ReflectionFormatter.cs index ce94590..501b2cc 100644 --- a/OdinSerializer/Core/Formatters/ReflectionFormatter.cs +++ b/OdinSerializer/Core/Formatters/ReflectionFormatter.cs @@ -18,9 +18,9 @@ namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using System; using System.Reflection; - using System.Runtime.Serialization; /// /// Final fallback formatter for all types which have no other formatters. This formatter relies on reflection to work, and is thus comparatively slow and creates more garbage than a custom formatter. @@ -69,7 +69,7 @@ protected override void DeserializeImplementation(ref T value, IDataReader reade if (members.TryGetValue(name, out member) == false) { - reader.Context.Config.DebugContext.LogWarning("Lost serialization data for entry \"" + name + "\" of type \"" + entryType + "\"in node \"" + reader.CurrentNodeName + "\"."); + reader.Context.Config.DebugContext.LogWarning("Lost serialization data for entry \"" + name + "\" of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" because a serialized member of that name could not be found in type " + typeof(T).GetNiceFullName() + "."); reader.SkipEntry(); continue; } @@ -121,4 +121,76 @@ protected override void SerializeImplementation(ref T value, IDataWriter writer) } } } + + public class WeakReflectionFormatter : WeakBaseFormatter + { + public WeakReflectionFormatter(Type serializedType) : base(serializedType) + { + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + var members = FormatterUtilities.GetSerializableMembersMap(this.SerializedType, reader.Context.Config.SerializationPolicy); + + EntryType entryType; + string name; + + while ((entryType = reader.PeekEntry(out name)) != EntryType.EndOfNode && entryType != EntryType.EndOfArray && entryType != EntryType.EndOfStream) + { + if (string.IsNullOrEmpty(name)) + { + reader.Context.Config.DebugContext.LogError("Entry of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" is missing a name."); + reader.SkipEntry(); + continue; + } + + MemberInfo member; + + if (members.TryGetValue(name, out member) == false) + { + reader.Context.Config.DebugContext.LogWarning("Lost serialization data for entry \"" + name + "\" of type \"" + entryType + "\" in node \"" + reader.CurrentNodeName + "\" because a serialized member of that name could not be found in type " + this.SerializedType.GetNiceFullName() + "."); + reader.SkipEntry(); + continue; + } + + Type expectedType = FormatterUtilities.GetContainedType(member); + + try + { + var serializer = Serializer.Get(expectedType); + object entryValue = serializer.ReadValueWeak(reader); + FormatterUtilities.SetMemberValue(member, value, entryValue); + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + } + } + + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + var members = FormatterUtilities.GetSerializableMembers(this.SerializedType, writer.Context.Config.SerializationPolicy); + + for (int i = 0; i < members.Length; i++) + { + var member = members[i]; + Type type; + var memberValue = FormatterUtilities.GetMemberValue(member, value); + + type = FormatterUtilities.GetContainedType(member); + + var serializer = Serializer.Get(type); + + try + { + serializer.WriteValueWeak(member.Name, memberValue, writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/ReflectionFormatter.cs.meta b/OdinSerializer/Core/Formatters/ReflectionFormatter.cs.meta index 06bd875..5ec1412 100644 --- a/OdinSerializer/Core/Formatters/ReflectionFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/ReflectionFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2c79d9aac41320abd8e2910e33630da1 +guid: 5a0591da7b3a773a3d1d84d1aa71403d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/ReflectionOrEmittedBaseFormatter.cs.meta b/OdinSerializer/Core/Formatters/ReflectionOrEmittedBaseFormatter.cs.meta index 4663d2b..ee3f161 100644 --- a/OdinSerializer/Core/Formatters/ReflectionOrEmittedBaseFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/ReflectionOrEmittedBaseFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 31b0f6f427611a8f26956fd68c07b402 +guid: 18ce9356ce14e94ada943ae2ca0d8f78 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs b/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs index 0819b0c..daf28da 100644 --- a/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs +++ b/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs @@ -18,6 +18,8 @@ namespace ToolBox.Serialization.OdinSerializer { + using System; + /// /// Formatter for types that implement the interface. /// @@ -41,4 +43,27 @@ protected override void SerializeImplementation(ref T value, IDataWriter writer) value.Serialize(writer); } } + + public sealed class WeakSelfFormatterFormatter : WeakBaseFormatter + { + public WeakSelfFormatterFormatter(Type serializedType) : base(serializedType) + { + } + + /// + /// Calls on the value to deserialize. + /// + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + ((ISelfFormatter)value).Deserialize(reader); + } + + /// + /// Calls on the value to deserialize. + /// + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + ((ISelfFormatter)value).Serialize(writer); + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs.meta b/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs.meta index 7546f9f..8298626 100644 --- a/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/SelfFormatterFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e51d8184cb11cf9171aec7f3ce8486d +guid: 48dd526b9b377fd6dc48087349f73c47 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/SerializableFormatter.cs b/OdinSerializer/Core/Formatters/SerializableFormatter.cs index 4b1e2bb..b30b701 100644 --- a/OdinSerializer/Core/Formatters/SerializableFormatter.cs +++ b/OdinSerializer/Core/Formatters/SerializableFormatter.cs @@ -128,8 +128,15 @@ protected override void SerializeImplementation(ref T value, IDataWriter writer) { if (SerializableFormatter.ISerializableConstructor != null) { + // Don't have GetType() in the below lines as a strongly typed T value, since + // people can "override" (shadow) GetType() on derived classes with the "new" + // operator. By referencing the type as a System.Object, we ensure the correct + // GetType() method is always called. + // + // (Yes, this has actually happened, and this was done to fix it.) + var serializable = value as ISerializable; - var info = new SerializationInfo(value.GetType(), writer.Context.FormatterConverter); + var info = new SerializationInfo((value as object).GetType(), writer.Context.FormatterConverter); try { @@ -236,4 +243,201 @@ private void WriteSerializationInfo(SerializationInfo info, IDataWriter writer) } } } + + public sealed class WeakSerializableFormatter : WeakBaseFormatter + { + private readonly Func ISerializableConstructor; + private readonly WeakReflectionFormatter ReflectionFormatter; + + public WeakSerializableFormatter(Type serializedType) : base(serializedType) + { + var current = serializedType; + ConstructorInfo constructor = null; + + do + { + constructor = current.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(SerializationInfo), typeof(StreamingContext) }, null); + current = current.BaseType; + } + while (constructor == null && current != typeof(object) && current != null); + + if (constructor != null) + { + // TODO: Fancy compiled delegate + ISerializableConstructor = (info, context) => + { + ISerializable obj = (ISerializable)FormatterServices.GetUninitializedObject(this.SerializedType); + constructor.Invoke(obj, new object[] { info, context }); + return obj; + }; + } + else + { + DefaultLoggers.DefaultLogger.LogWarning("Type " + this.SerializedType.Name + " implements the interface ISerializable but does not implement the required constructor with signature " + this.SerializedType.Name + "(SerializationInfo info, StreamingContext context). The interface declaration will be ignored, and the formatter fallbacks to reflection."); + ReflectionFormatter = new WeakReflectionFormatter(this.SerializedType); + } + } + + /// + /// Get an uninitialized object of type . WARNING: If you override this and return null, the object's ID will not be automatically registered and its OnDeserializing callbacks will not be automatically called, before deserialization begins. + /// You will have to call and immediately after creating the object yourself during deserialization. + /// + /// + /// An uninitialized object of type . + /// + protected override object GetUninitializedObject() + { + return null; + } + + /// + /// Provides the actual implementation for deserializing a value of type . + /// + /// The uninitialized value to serialize into. This value will have been created earlier using . + /// The reader to deserialize with. + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + if (this.ISerializableConstructor != null) + { + var info = this.ReadSerializationInfo(reader); + + if (info != null) + { + try + { + value = this.ISerializableConstructor(info, reader.Context.StreamingContext); + + this.InvokeOnDeserializingCallbacks(value, reader.Context); + + if (IsValueType == false) + { + this.RegisterReferenceID(value, reader); + } + + return; + } + catch (Exception ex) + { + reader.Context.Config.DebugContext.LogException(ex); + } + } + } + else + { + value = this.ReflectionFormatter.Deserialize(reader); + + this.InvokeOnDeserializingCallbacks(value, reader.Context); + + if (this.IsValueType == false) + { + this.RegisterReferenceID(value, reader); + } + } + } + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + if (this.ISerializableConstructor != null) + { + var serializable = value as ISerializable; + var info = new SerializationInfo(value.GetType(), writer.Context.FormatterConverter); + + try + { + serializable.GetObjectData(info, writer.Context.StreamingContext); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + + this.WriteSerializationInfo(info, writer); + } + else + { + this.ReflectionFormatter.Serialize(value, writer); + } + } + + private SerializationInfo ReadSerializationInfo(IDataReader reader) + { + string name; + EntryType entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + + SerializationInfo info = new SerializationInfo(this.SerializedType, reader.Context.FormatterConverter); + + for (int i = 0; i < length; i++) + { + Type type = null; + entry = reader.PeekEntry(out name); + + if (entry == EntryType.String && name == "type") + { + string typeName; + reader.ReadString(out typeName); + type = reader.Context.Binder.BindToType(typeName, reader.Context.Config.DebugContext); + } + + if (type == null) + { + reader.SkipEntry(); + continue; + } + + entry = reader.PeekEntry(out name); + + var readerWriter = Serializer.Get(type); + object value = readerWriter.ReadValueWeak(reader); + info.AddValue(name, value); + } + + return info; + } + finally + { + reader.ExitArray(); + } + } + + return null; + } + + private void WriteSerializationInfo(SerializationInfo info, IDataWriter writer) + { + try + { + writer.BeginArrayNode(info.MemberCount); + + foreach (var entry in info) + { + try + { + writer.WriteString("type", writer.Context.Binder.BindToName(entry.ObjectType, writer.Context.Config.DebugContext)); + var readerWriter = Serializer.Get(entry.ObjectType); + readerWriter.WriteValueWeak(entry.Name, entry.Value, writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/SerializableFormatter.cs.meta b/OdinSerializer/Core/Formatters/SerializableFormatter.cs.meta index cf98ee1..bddf398 100644 --- a/OdinSerializer/Core/Formatters/SerializableFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/SerializableFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c98a6873875e1825170c2542c362d90c +guid: 73d1c6351a5cef85efdb6581208c4f94 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/StackFormatter.cs b/OdinSerializer/Core/Formatters/StackFormatter.cs index 6531ba2..023280a 100644 --- a/OdinSerializer/Core/Formatters/StackFormatter.cs +++ b/OdinSerializer/Core/Formatters/StackFormatter.cs @@ -18,13 +18,15 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(StackFormatter<,>))] +[assembly: RegisterFormatter(typeof(StackFormatter<,>), weakFallback: typeof(WeakStackFormatter))] namespace ToolBox.Serialization.OdinSerializer { using Utilities; using System; + using System.Collections; using System.Collections.Generic; + using System.Reflection; /// /// Custom generic formatter for the generic type definition and types derived from it. @@ -160,4 +162,128 @@ protected override void SerializeImplementation(ref TStack value, IDataWriter wr } } } + + public class WeakStackFormatter : WeakBaseFormatter + { + private readonly Serializer ElementSerializer; + private readonly bool IsPlainStack; + private readonly MethodInfo PushMethod; + + public WeakStackFormatter(Type serializedType) : base(serializedType) + { + var args = serializedType.GetArgumentsOfInheritedOpenGenericClass(typeof(Stack<>)); + this.ElementSerializer = Serializer.Get(args[0]); + this.IsPlainStack = serializedType.IsGenericType && serializedType.GetGenericTypeDefinition() == typeof(Stack<>); + + if (this.PushMethod == null) + { + throw new SerializationAbortException("Can't serialize type '" + serializedType.GetNiceFullName() + "' because no proper Push method was found."); + } + } + + protected override object GetUninitializedObject() + { + return null; + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.StartOfArray) + { + try + { + long length; + reader.EnterArray(out length); + + if (IsPlainStack) + { + value = Activator.CreateInstance(this.SerializedType, (int)length); + } + else + { + value = Activator.CreateInstance(this.SerializedType); + } + + // We must remember to register the stack reference ourselves, since we return null in GetUninitializedObject + this.RegisterReferenceID(value, reader); + + var pushParams = new object[1]; + + // There aren't any OnDeserializing callbacks on stacks. + // Hence we don't invoke this.InvokeOnDeserializingCallbacks(value, reader, context); + for (int i = 0; i < length; i++) + { + if (reader.PeekEntry(out name) == EntryType.EndOfArray) + { + reader.Context.Config.DebugContext.LogError("Reached end of array after " + i + " elements, when " + length + " elements were expected."); + break; + } + + pushParams[0] = this.ElementSerializer.ReadValueWeak(reader); + PushMethod.Invoke(value, pushParams); + + if (reader.IsInArrayNode == false) + { + // Something has gone wrong + reader.Context.Config.DebugContext.LogError("Reading array went wrong. Data dump: " + reader.GetDataDump()); + break; + } + } + } + finally + { + reader.ExitArray(); + } + } + else + { + reader.SkipEntry(); + } + } + + /// + /// Provides the actual implementation for serializing a value of type . + /// + /// The value to serialize. + /// The writer to serialize with. + protected override void SerializeImplementation(ref object value, IDataWriter writer) + { + try + { + ICollection collection = (ICollection)value; + + writer.BeginArrayNode(collection.Count); + + using (var listCache = Cache>.Claim()) + { + var list = listCache.Value; + list.Clear(); + + foreach (var element in collection) + { + list.Add(element); + } + + for (int i = list.Count - 1; i >= 0; i--) + { + try + { + this.ElementSerializer.WriteValueWeak(list[i], writer); + } + catch (Exception ex) + { + writer.Context.Config.DebugContext.LogException(ex); + } + } + } + } + finally + { + writer.EndArrayNode(); + } + } + } } \ No newline at end of file diff --git a/OdinSerializer/Core/Formatters/StackFormatter.cs.meta b/OdinSerializer/Core/Formatters/StackFormatter.cs.meta index bd33d58..a863a14 100644 --- a/OdinSerializer/Core/Formatters/StackFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/StackFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1cc91d8b982146988024eeb0e598578b +guid: ea5ff2d9b5df3406d99b844756aa8ffd MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/TimeSpanFormatter.cs.meta b/OdinSerializer/Core/Formatters/TimeSpanFormatter.cs.meta index 144451e..2a3db26 100644 --- a/OdinSerializer/Core/Formatters/TimeSpanFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/TimeSpanFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 104386dd61e3cd9a48bdf4fd569d5cd3 +guid: dbfb8322a7bcf04e3b040b9f62e202d2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/TypeFormatter.cs.meta b/OdinSerializer/Core/Formatters/TypeFormatter.cs.meta index 1850499..67a3749 100644 --- a/OdinSerializer/Core/Formatters/TypeFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/TypeFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4f40649fbb4cc0f022a00f2e0ad868fa +guid: 70a0c1ae2653dadfe76c81a9775a1406 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Formatters/VersionFormatter.cs.meta b/OdinSerializer/Core/Formatters/VersionFormatter.cs.meta index 535d5ee..62066fa 100644 --- a/OdinSerializer/Core/Formatters/VersionFormatter.cs.meta +++ b/OdinSerializer/Core/Formatters/VersionFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ec4a83dc7ab665f20bcf05e6ff2bddaa +guid: 9f5212401202e0c378c2d20c36522292 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc.meta b/OdinSerializer/Core/Misc.meta index 7a49c7f..a2c5c64 100644 --- a/OdinSerializer/Core/Misc.meta +++ b/OdinSerializer/Core/Misc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3c0af62b8c9cbe043930d86fbc35c929 +guid: d369292bdf1068846b405ca263293d10 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/Misc/AllowDeserializeInvalidDataAttribute.cs.meta b/OdinSerializer/Core/Misc/AllowDeserializeInvalidDataAttribute.cs.meta index f3f31f3..a7e406f 100644 --- a/OdinSerializer/Core/Misc/AllowDeserializeInvalidDataAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/AllowDeserializeInvalidDataAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 15561f286f0e4bdc32f3d08187beb5c3 +guid: a1a191d5a3f3ac3623992998e84b2823 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/AlwaysFormatsSelfAttribute.cs.meta b/OdinSerializer/Core/Misc/AlwaysFormatsSelfAttribute.cs.meta index ac4ef34..68bff6e 100644 --- a/OdinSerializer/Core/Misc/AlwaysFormatsSelfAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/AlwaysFormatsSelfAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 908613df151eb8c140dbad0876931b4d +guid: 81f4e093d8901efb76e6aee8ffb57433 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ArchitectureInfo.cs.meta b/OdinSerializer/Core/Misc/ArchitectureInfo.cs.meta index 94c2e32..1fc6112 100644 --- a/OdinSerializer/Core/Misc/ArchitectureInfo.cs.meta +++ b/OdinSerializer/Core/Misc/ArchitectureInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ecd25315e2dce51ff0666a4304be0476 +guid: 9e96bd6be961e77decb91e26b05d548d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/Buffer.cs.meta b/OdinSerializer/Core/Misc/Buffer.cs.meta index 12fc51f..d44760f 100644 --- a/OdinSerializer/Core/Misc/Buffer.cs.meta +++ b/OdinSerializer/Core/Misc/Buffer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fda1f43d69ca630e1a36ab491f21a4d7 +guid: 391501b1c4202e0c659200440daa3e3d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/CachedMemoryStream.cs.meta b/OdinSerializer/Core/Misc/CachedMemoryStream.cs.meta index 9b2e535..f6f39e1 100644 --- a/OdinSerializer/Core/Misc/CachedMemoryStream.cs.meta +++ b/OdinSerializer/Core/Misc/CachedMemoryStream.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dbebe527c2bd6a911ad1500fc2adf705 +guid: 8b22a8777a2a2e8ac9e358e4aabcada5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/CustomFormatterAttribute.cs.meta b/OdinSerializer/Core/Misc/CustomFormatterAttribute.cs.meta index fea9f08..2c70d59 100644 --- a/OdinSerializer/Core/Misc/CustomFormatterAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/CustomFormatterAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 514fc47ba2db8945f873f2151b202d4c +guid: 1230bd68e831ed72a1ba7944805d4619 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/CustomGenericFormatterAttribute.cs.meta b/OdinSerializer/Core/Misc/CustomGenericFormatterAttribute.cs.meta index 3225df1..463eebe 100644 --- a/OdinSerializer/Core/Misc/CustomGenericFormatterAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/CustomGenericFormatterAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cd3a47ca784d41492dcad6264a69a25c +guid: 2a53ebe596457578de10102286c8c33e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/CustomLogger.cs.meta b/OdinSerializer/Core/Misc/CustomLogger.cs.meta index 292a767..fb42016 100644 --- a/OdinSerializer/Core/Misc/CustomLogger.cs.meta +++ b/OdinSerializer/Core/Misc/CustomLogger.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: eb1e1be239bca9873d5df2e3ffb13f24 +guid: b7659e23098a0f00958dd790de140de9 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/CustomSerializationPolicy.cs.meta b/OdinSerializer/Core/Misc/CustomSerializationPolicy.cs.meta index ee53f58..4dc0b14 100644 --- a/OdinSerializer/Core/Misc/CustomSerializationPolicy.cs.meta +++ b/OdinSerializer/Core/Misc/CustomSerializationPolicy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 753318f51a2837ecbbbe3acc6758c14a +guid: 7c048d28ce2cbcbb8277c1c9dffa9013 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/DataFormat.cs.meta b/OdinSerializer/Core/Misc/DataFormat.cs.meta index bccdd3e..b30cd10 100644 --- a/OdinSerializer/Core/Misc/DataFormat.cs.meta +++ b/OdinSerializer/Core/Misc/DataFormat.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ea69db5df9a76f3c31bac05e05db2dbf +guid: 94f21f862e976b23399807ca60618ddf MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/DefaultLoggers.cs.meta b/OdinSerializer/Core/Misc/DefaultLoggers.cs.meta index b9256d9..6a09218 100644 --- a/OdinSerializer/Core/Misc/DefaultLoggers.cs.meta +++ b/OdinSerializer/Core/Misc/DefaultLoggers.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 93fb1963b681b9d5f15762f06a26d867 +guid: a9cafdf065f7a1f552799a5cda92d056 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs b/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs index 3ed6172..c04c998 100644 --- a/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs +++ b/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs @@ -106,6 +106,12 @@ static DefaultSerializationBinder() assembliesQueuedForRegister.Add(assembly); } } + + lock (ASSEMBLY_LOOKUP_LOCK) + { + customTypeNameToTypeBindings["System.Reflection.MonoMethod"] = typeof(MethodInfo); + customTypeNameToTypeBindings["System.Reflection.MonoMethod, mscorlib"] = typeof(MethodInfo); + } } private static void RegisterAllQueuedAssembliesRepeating() diff --git a/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs.meta b/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs.meta index 226c141..e67407c 100644 --- a/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs.meta +++ b/OdinSerializer/Core/Misc/DefaultSerializationBinder.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3bbf2f460763ca7749ec5afd84ee2c8c +guid: 7054f45a751cccb30fdad8835f5402ed MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/DeserializationContext.cs b/OdinSerializer/Core/Misc/DeserializationContext.cs index 7c20c82..d2dcec3 100644 --- a/OdinSerializer/Core/Misc/DeserializationContext.cs +++ b/OdinSerializer/Core/Misc/DeserializationContext.cs @@ -80,6 +80,11 @@ public DeserializationContext(StreamingContext context, FormatterConverter forma this.Reset(); } + public DeserializationContext(IExternalIndexReferenceResolver indexReferenceResolver) : this() + { + IndexReferenceResolver = indexReferenceResolver; + } + /// /// Gets or sets the context's type binder. /// diff --git a/OdinSerializer/Core/Misc/DeserializationContext.cs.meta b/OdinSerializer/Core/Misc/DeserializationContext.cs.meta index d822244..41f7e4d 100644 --- a/OdinSerializer/Core/Misc/DeserializationContext.cs.meta +++ b/OdinSerializer/Core/Misc/DeserializationContext.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c4f30ee8364be1afbcf1cb45efdc69be +guid: bd60894dbaf5133e6e02ad30db05ea61 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/EmittedAssemblyAttribute.cs.meta b/OdinSerializer/Core/Misc/EmittedAssemblyAttribute.cs.meta index 5398592..604dbb0 100644 --- a/OdinSerializer/Core/Misc/EmittedAssemblyAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/EmittedAssemblyAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4db048211b335931aec3df20461bcc7f +guid: 94c69b6e3fc36bd986130467208d02a4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/EntryType.cs.meta b/OdinSerializer/Core/Misc/EntryType.cs.meta index e5d19ec..8e37d19 100644 --- a/OdinSerializer/Core/Misc/EntryType.cs.meta +++ b/OdinSerializer/Core/Misc/EntryType.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 90005ff1f2ae853832c82b3aced9a1dc +guid: 3d68fbc8fa11a73678131d56bb23a17e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ErrorHandlingPolicy.cs.meta b/OdinSerializer/Core/Misc/ErrorHandlingPolicy.cs.meta index 9fef2b9..9a7908a 100644 --- a/OdinSerializer/Core/Misc/ErrorHandlingPolicy.cs.meta +++ b/OdinSerializer/Core/Misc/ErrorHandlingPolicy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c4cde8f46393ecdcd60735292d7ae7ec +guid: 2d01082e89c258db410e67ce1512d0b2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ExcludeDataFromInspectorAttribute.cs.meta b/OdinSerializer/Core/Misc/ExcludeDataFromInspectorAttribute.cs.meta index c9fb9b2..ccab9ec 100644 --- a/OdinSerializer/Core/Misc/ExcludeDataFromInspectorAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/ExcludeDataFromInspectorAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 63d0bbfe95edbe25c413299087bc3545 +guid: 4263174bc4849019434ffb76277cf6fb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/FormatterLocationStep.cs.meta b/OdinSerializer/Core/Misc/FormatterLocationStep.cs.meta index adb6b4f..9db4cfb 100644 --- a/OdinSerializer/Core/Misc/FormatterLocationStep.cs.meta +++ b/OdinSerializer/Core/Misc/FormatterLocationStep.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4186c3e75ca61ec414665d67bc00f2cb +guid: 345cab2395be6ad266ebcbab31897ae6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/FormatterUtilities.cs.meta b/OdinSerializer/Core/Misc/FormatterUtilities.cs.meta index 8747f3f..2e21706 100644 --- a/OdinSerializer/Core/Misc/FormatterUtilities.cs.meta +++ b/OdinSerializer/Core/Misc/FormatterUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c8b7de9af7305dc1fe9000cc652b4b04 +guid: 618b4f3878f43a6b04e29f073763a723 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/IAskIfCanFormatTypes.cs.meta b/OdinSerializer/Core/Misc/IAskIfCanFormatTypes.cs.meta index 0b619e1..21332c3 100644 --- a/OdinSerializer/Core/Misc/IAskIfCanFormatTypes.cs.meta +++ b/OdinSerializer/Core/Misc/IAskIfCanFormatTypes.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ea214b1e6e10341af10298a000611b59 +guid: ff6a40cdc72f6fabfc05d399b689ef79 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/IExternalGuidReferenceResolver.cs.meta b/OdinSerializer/Core/Misc/IExternalGuidReferenceResolver.cs.meta index 9c25aa5..d62e41c 100644 --- a/OdinSerializer/Core/Misc/IExternalGuidReferenceResolver.cs.meta +++ b/OdinSerializer/Core/Misc/IExternalGuidReferenceResolver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 081d7187f22976baa42e2e0497a6c1e5 +guid: ad9e8df68f584e8f311e4fa9538e973e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/IExternalIndexReferenceResolver.cs.meta b/OdinSerializer/Core/Misc/IExternalIndexReferenceResolver.cs.meta index 539d644..0194b72 100644 --- a/OdinSerializer/Core/Misc/IExternalIndexReferenceResolver.cs.meta +++ b/OdinSerializer/Core/Misc/IExternalIndexReferenceResolver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 61f551b2704645c7d90e7970607e4b8f +guid: 9873baffde8b622983d547f4d2ef1b4d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/IExternalStringReferenceResolver.cs.meta b/OdinSerializer/Core/Misc/IExternalStringReferenceResolver.cs.meta index 8a75754..675e796 100644 --- a/OdinSerializer/Core/Misc/IExternalStringReferenceResolver.cs.meta +++ b/OdinSerializer/Core/Misc/IExternalStringReferenceResolver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2dad660b897f3a62859bfbd81b9ce92e +guid: d633ccb54c73f83e470869e9448093ac MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ILogger.cs.meta b/OdinSerializer/Core/Misc/ILogger.cs.meta index 19d43a3..5dea7e1 100644 --- a/OdinSerializer/Core/Misc/ILogger.cs.meta +++ b/OdinSerializer/Core/Misc/ILogger.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1056d25f39a34fc8e7fa19a5220358bb +guid: 06038c8617a6f155f016443d25c27aa2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ISelfFormatter.cs.meta b/OdinSerializer/Core/Misc/ISelfFormatter.cs.meta index d5323a9..0afde44 100644 --- a/OdinSerializer/Core/Misc/ISelfFormatter.cs.meta +++ b/OdinSerializer/Core/Misc/ISelfFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 95ea83151b66b9663ca438a63aa219b3 +guid: f05c64a6babec6e94d438a5e259b302b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ISerializationPolicy.cs.meta b/OdinSerializer/Core/Misc/ISerializationPolicy.cs.meta index 22c2172..9fd66e3 100644 --- a/OdinSerializer/Core/Misc/ISerializationPolicy.cs.meta +++ b/OdinSerializer/Core/Misc/ISerializationPolicy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2cd7797892d51a13f89117e5c56f5cdf +guid: aa01448a764fca173f6d7cdbdd4ea039 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/LoggingPolicy.cs.meta b/OdinSerializer/Core/Misc/LoggingPolicy.cs.meta index 571706b..20e9d14 100644 --- a/OdinSerializer/Core/Misc/LoggingPolicy.cs.meta +++ b/OdinSerializer/Core/Misc/LoggingPolicy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 85e0b1765636d6be96b7760fb73cc248 +guid: 8a8a0735b4663d78637ac10a17a4bd1f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/NodeInfo.cs.meta b/OdinSerializer/Core/Misc/NodeInfo.cs.meta index aa683e3..95998c2 100644 --- a/OdinSerializer/Core/Misc/NodeInfo.cs.meta +++ b/OdinSerializer/Core/Misc/NodeInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f46076deca79d656e5fd127bd143e7ab +guid: 6975a8f56e91b72d928a4091494035bb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/OdinSerializeAttribute.cs.meta b/OdinSerializer/Core/Misc/OdinSerializeAttribute.cs.meta index 4845ca3..1f6f238 100644 --- a/OdinSerializer/Core/Misc/OdinSerializeAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/OdinSerializeAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 839c9143a982625c586c60e1b2de5fee +guid: 1fd1cd2b26ec720dde305bae8a352bf7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/PrefabModification.cs.meta b/OdinSerializer/Core/Misc/PrefabModification.cs.meta index 8f60014..37aee7a 100644 --- a/OdinSerializer/Core/Misc/PrefabModification.cs.meta +++ b/OdinSerializer/Core/Misc/PrefabModification.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 113ca5f60d4996acbc71282919070600 +guid: c55f71c23cef45e2025ba7cc04b93367 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/PrefabModificationType.cs.meta b/OdinSerializer/Core/Misc/PrefabModificationType.cs.meta index 606799c..ac83e10 100644 --- a/OdinSerializer/Core/Misc/PrefabModificationType.cs.meta +++ b/OdinSerializer/Core/Misc/PrefabModificationType.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 78380b98de5a74dcafadd82d80610ffd +guid: 99a6fba3ad0fcee2bb8df4a07f40cfc3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/PreviouslySerializedAsAttribute.cs.meta b/OdinSerializer/Core/Misc/PreviouslySerializedAsAttribute.cs.meta index 39c6148..c2b917c 100644 --- a/OdinSerializer/Core/Misc/PreviouslySerializedAsAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/PreviouslySerializedAsAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c9b782a5873856d3910ea00689532bf2 +guid: 7417ec884af3115038e6ee6326b93fd6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/ProperBitConverter.cs.meta b/OdinSerializer/Core/Misc/ProperBitConverter.cs.meta index b21fcf8..50b15b5 100644 --- a/OdinSerializer/Core/Misc/ProperBitConverter.cs.meta +++ b/OdinSerializer/Core/Misc/ProperBitConverter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ae2285574c85baa279ab0e356ad54830 +guid: 41468d0d936668071e9c398d53a1d3ed MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs b/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs index a56a265..e3a4902 100644 --- a/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs +++ b/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs @@ -24,6 +24,7 @@ namespace ToolBox.Serialization.OdinSerializer public class RegisterFormatterAttribute : Attribute { public Type FormatterType { get; private set; } + public Type WeakFallback { get; private set; } public int Priority { get; private set; } public RegisterFormatterAttribute(Type formatterType, int priority = 0) @@ -31,5 +32,12 @@ public RegisterFormatterAttribute(Type formatterType, int priority = 0) this.FormatterType = formatterType; this.Priority = priority; } + + public RegisterFormatterAttribute(Type formatterType, Type weakFallback, int priority = 0) + { + this.FormatterType = formatterType; + this.WeakFallback = weakFallback; + this.Priority = priority; + } } } \ No newline at end of file diff --git a/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs.meta b/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs.meta index 70dd3da..53f6c75 100644 --- a/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/RegisterFormatterAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1af1de19921b4dcd3c6928199c352d3d +guid: 92a9394fb735645f2647d04baaceeade MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/RegisterFormatterLocatorAttribute.cs.meta b/OdinSerializer/Core/Misc/RegisterFormatterLocatorAttribute.cs.meta index ebc13da..953494b 100644 --- a/OdinSerializer/Core/Misc/RegisterFormatterLocatorAttribute.cs.meta +++ b/OdinSerializer/Core/Misc/RegisterFormatterLocatorAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6e944884c461f7f6a937dffdced30490 +guid: 986d976f88440fe556da0c188ba12006 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/SerializationAbortException.cs b/OdinSerializer/Core/Misc/SerializationAbortException.cs index e88fed5..af14131 100644 --- a/OdinSerializer/Core/Misc/SerializationAbortException.cs +++ b/OdinSerializer/Core/Misc/SerializationAbortException.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// +// // Copyright (c) 2018 Sirenix IVS // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,10 +16,10 @@ // //----------------------------------------------------------------------- -using System; - namespace ToolBox.Serialization.OdinSerializer { + using System; + /// /// An exception thrown when the serialization system has encountered an issue so severe that serialization is being aborted. If this exception is caught in the serialization system somewhere, it should be rethrown. /// diff --git a/OdinSerializer/Core/Misc/SerializationAbortException.cs.meta b/OdinSerializer/Core/Misc/SerializationAbortException.cs.meta index 07169a6..d093909 100644 --- a/OdinSerializer/Core/Misc/SerializationAbortException.cs.meta +++ b/OdinSerializer/Core/Misc/SerializationAbortException.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 44264e3a34d47ae974fbd43d60e2e694 +guid: b6aa07a8b398138807e545d2f7ab800e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/SerializationConfig.cs b/OdinSerializer/Core/Misc/SerializationConfig.cs index 37fc5f4..edf7206 100644 --- a/OdinSerializer/Core/Misc/SerializationConfig.cs +++ b/OdinSerializer/Core/Misc/SerializationConfig.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// +// // Copyright (c) 2018 Sirenix IVS // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -16,10 +16,10 @@ // //----------------------------------------------------------------------- -using System; - namespace ToolBox.Serialization.OdinSerializer { + using System; + /// /// Defines the configuration during serialization and deserialization. This class is thread-safe. /// diff --git a/OdinSerializer/Core/Misc/SerializationConfig.cs.meta b/OdinSerializer/Core/Misc/SerializationConfig.cs.meta index 155ab04..0e31987 100644 --- a/OdinSerializer/Core/Misc/SerializationConfig.cs.meta +++ b/OdinSerializer/Core/Misc/SerializationConfig.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c2b4caf868e4fe4c5252406a0748242a +guid: 96122c4f7993fced95c9a0eb25c8048f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/SerializationContext.cs b/OdinSerializer/Core/Misc/SerializationContext.cs index 3e4204a..4725cfc 100644 --- a/OdinSerializer/Core/Misc/SerializationContext.cs +++ b/OdinSerializer/Core/Misc/SerializationContext.cs @@ -80,6 +80,11 @@ public SerializationContext(StreamingContext context, FormatterConverter formatt this.ResetToDefault(); } + public SerializationContext(IExternalIndexReferenceResolver indexReferenceResolver) : this() + { + IndexReferenceResolver = indexReferenceResolver; + } + /// /// Gets or sets the context's type binder. /// diff --git a/OdinSerializer/Core/Misc/SerializationContext.cs.meta b/OdinSerializer/Core/Misc/SerializationContext.cs.meta index 3163c30..da9cd21 100644 --- a/OdinSerializer/Core/Misc/SerializationContext.cs.meta +++ b/OdinSerializer/Core/Misc/SerializationContext.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ab938e9c9e431ee8e23dea2beecb6697 +guid: 2640ed6582586886dfafdd912bfaf623 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/SerializationPolicies.cs.meta b/OdinSerializer/Core/Misc/SerializationPolicies.cs.meta index b6c46c9..360b355 100644 --- a/OdinSerializer/Core/Misc/SerializationPolicies.cs.meta +++ b/OdinSerializer/Core/Misc/SerializationPolicies.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 012a9bfd44a557d59cb58791493c58bb +guid: fecea0c87b04d53b7b002e9ceb7bb94d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/SerializationUtility.cs b/OdinSerializer/Core/Misc/SerializationUtility.cs index 38e788e..a09b186 100644 --- a/OdinSerializer/Core/Misc/SerializationUtility.cs +++ b/OdinSerializer/Core/Misc/SerializationUtility.cs @@ -203,7 +203,24 @@ public static void SerializeValueWeak(object value, IDataWriter writer, out List /// The writer to use. public static void SerializeValue(T value, IDataWriter writer) { - Serializer.Get().WriteValue(value, writer); + if (EmitUtilities.CanEmit) + { + Serializer.Get().WriteValue(value, writer); + } + else + { + var serializer = Serializer.Get(typeof(T)); + var strong = serializer as Serializer; + + if (strong != null) + { + strong.WriteValue(value, writer); + } + else + { + serializer.WriteValueWeak(value, writer); + } + } writer.FlushToStream(); } @@ -219,7 +236,24 @@ public static void SerializeValue(T value, IDataWriter writer, out List.Claim()) { writer.Context.IndexReferenceResolver = unityResolver.Value; - Serializer.Get().WriteValue(value, writer); + if (EmitUtilities.CanEmit) + { + Serializer.Get().WriteValue(value, writer); + } + else + { + var serializer = Serializer.Get(typeof(T)); + var strong = serializer as Serializer; + + if (strong != null) + { + strong.WriteValue(value, writer); + } + else + { + serializer.WriteValueWeak(value, writer); + } + } writer.FlushToStream(); unityObjects = unityResolver.Value.GetReferencedUnityObjects(); } @@ -437,7 +471,7 @@ public static byte[] SerializeValue(T value, DataFormat format, out ListThe deserialized value. public static object DeserializeValueWeak(IDataReader reader) { - return Serializer.Get().ReadValueWeak(reader); + return Serializer.Get(typeof(object)).ReadValueWeak(reader); } /// @@ -454,7 +488,7 @@ public static object DeserializeValueWeak(IDataReader reader, List().ReadValueWeak(reader); + return Serializer.Get(typeof(object)).ReadValueWeak(reader); } } @@ -466,7 +500,24 @@ public static object DeserializeValueWeak(IDataReader reader, ListThe deserialized value. public static T DeserializeValue(IDataReader reader) { - return Serializer.Get().ReadValue(reader); + if (EmitUtilities.CanEmit) + { + return Serializer.Get().ReadValue(reader); + } + else + { + var serializer = Serializer.Get(typeof(T)); + var strong = serializer as Serializer; + + if (strong != null) + { + return strong.ReadValue(reader); + } + else + { + return (T)serializer.ReadValueWeak(reader); + } + } } /// @@ -484,7 +535,25 @@ public static T DeserializeValue(IDataReader reader, List { unityResolver.Value.SetReferencedUnityObjects(referencedUnityObjects); reader.Context.IndexReferenceResolver = unityResolver.Value; - return Serializer.Get().ReadValue(reader); + + if (EmitUtilities.CanEmit) + { + return Serializer.Get().ReadValue(reader); + } + else + { + var serializer = Serializer.Get(typeof(T)); + var strong = serializer as Serializer; + + if (strong != null) + { + return strong.ReadValue(reader); + } + else + { + return (T)serializer.ReadValueWeak(reader); + } + } } } @@ -705,6 +774,8 @@ public static T DeserializeValue(byte[] bytes, DataFormat format, List /// Creates a deep copy of an object. Returns null if null. All Unity objects references will remain the same - they will not get copied. + /// Similarly, strings are not copied, nor are reflection types such as System.Type, or types derived from System.Reflection.MemberInfo, + /// System.Reflection.Assembly or System.Reflection.Module. /// public static object CreateCopy(object obj) { @@ -725,7 +796,10 @@ public static object CreateCopy(object obj) return obj; } - if (type.InheritsFrom(typeof(UnityEngine.Object))) + if (type.InheritsFrom(typeof(UnityEngine.Object)) + || type.InheritsFrom(typeof(System.Reflection.MemberInfo)) + || type.InheritsFrom(typeof(System.Reflection.Assembly)) + || type.InheritsFrom(typeof(System.Reflection.Module))) { return obj; } diff --git a/OdinSerializer/Core/Misc/SerializationUtility.cs.meta b/OdinSerializer/Core/Misc/SerializationUtility.cs.meta index b7c5927..453edbd 100644 --- a/OdinSerializer/Core/Misc/SerializationUtility.cs.meta +++ b/OdinSerializer/Core/Misc/SerializationUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5999bd7c8cda92e6d206e7d1db6defa9 +guid: 691515254000c4d40fd358b0d47ce617 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Misc/TwoWaySerializationBinder.cs.meta b/OdinSerializer/Core/Misc/TwoWaySerializationBinder.cs.meta index f60066c..0ae1132 100644 --- a/OdinSerializer/Core/Misc/TwoWaySerializationBinder.cs.meta +++ b/OdinSerializer/Core/Misc/TwoWaySerializationBinder.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5c4c80ef3ce9eb6175a68b27d9da7155 +guid: 876239bc947153f9fb3f6f295b403e11 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers.meta b/OdinSerializer/Core/Serializers.meta index 7c6523b..b6e37d4 100644 --- a/OdinSerializer/Core/Serializers.meta +++ b/OdinSerializer/Core/Serializers.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c620a85fc1738674cb025e3f2211ba67 +guid: ffea1b4eb36828649bd7bab90fadfd61 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Core/Serializers/AnySerializer.cs b/OdinSerializer/Core/Serializers/AnySerializer.cs new file mode 100644 index 0000000..9ba5021 --- /dev/null +++ b/OdinSerializer/Core/Serializers/AnySerializer.cs @@ -0,0 +1,707 @@ +//----------------------------------------------------------------------- +// +// 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. +// +//----------------------------------------------------------------------- + +namespace ToolBox.Serialization.OdinSerializer +{ + using Utilities; + using System; + using System.Collections.Generic; + + public sealed class AnySerializer : Serializer + { + private static readonly ISerializationPolicy UnityPolicy = SerializationPolicies.Unity; + private static readonly ISerializationPolicy StrictPolicy = SerializationPolicies.Strict; + private static readonly ISerializationPolicy EverythingPolicy = SerializationPolicies.Everything; + + private readonly Type SerializedType; + private readonly bool IsEnum; + private readonly bool IsValueType; + private readonly bool MayBeBoxedValueType; + private readonly bool IsAbstract; + private readonly bool IsNullable; + + private readonly bool AllowDeserializeInvalidData; + + private IFormatter UnityPolicyFormatter; + private IFormatter StrictPolicyFormatter; + private IFormatter EverythingPolicyFormatter; + + private readonly Dictionary FormattersByPolicy = new Dictionary(ReferenceEqualityComparer.Default); + private readonly object FormattersByPolicy_LOCK = new object(); + + public AnySerializer(Type serializedType) + { + this.SerializedType = serializedType; + this.IsEnum = this.SerializedType.IsEnum; + this.IsValueType = this.SerializedType.IsValueType; + this.MayBeBoxedValueType = this.SerializedType.IsInterface || this.SerializedType == typeof(object) || this.SerializedType == typeof(ValueType) || this.SerializedType == typeof(Enum); + this.IsAbstract = this.SerializedType.IsAbstract || this.SerializedType.IsInterface; + this.IsNullable = this.SerializedType.IsGenericType && this.SerializedType.GetGenericTypeDefinition() == typeof(Nullable<>); + this.AllowDeserializeInvalidData = this.SerializedType.IsDefined(typeof(AllowDeserializeInvalidDataAttribute), true); + } + + public override object ReadValueWeak(IDataReader reader) + { + if (IsEnum) + { + string name; + var entry = reader.PeekEntry(out name); + + if (entry == EntryType.Integer) + { + ulong value; + if (reader.ReadUInt64(out value) == false) + { + reader.Context.Config.DebugContext.LogWarning("Failed to read entry '" + name + "' of type " + entry.ToString()); + } + + return Enum.ToObject(this.SerializedType, value); + } + else + { + reader.Context.Config.DebugContext.LogWarning("Expected entry of type " + EntryType.Integer.ToString() + ", but got entry '" + name + "' of type " + entry.ToString()); + reader.SkipEntry(); + return Activator.CreateInstance(this.SerializedType); + } + } + else + { + var context = reader.Context; + + if (context.Config.SerializationPolicy.AllowNonSerializableTypes == false && this.SerializedType.IsSerializable == false) + { + context.Config.DebugContext.LogError("The type " + this.SerializedType.Name + " is not marked as serializable."); + return this.IsValueType ? Activator.CreateInstance(this.SerializedType) : null; + } + + bool exitNode = true; + + string name; + var entry = reader.PeekEntry(out name); + + if (this.IsValueType) + { + if (entry == EntryType.Null) + { + context.Config.DebugContext.LogWarning("Expecting complex struct of type " + this.SerializedType.GetNiceFullName() + " but got null value."); + reader.ReadNull(); + return Activator.CreateInstance(this.SerializedType); + } + else if (entry != EntryType.StartOfNode) + { + context.Config.DebugContext.LogWarning("Unexpected entry '" + name + "' of type " + entry.ToString() + ", when " + EntryType.StartOfNode + " was expected. A value has likely been lost."); + reader.SkipEntry(); + return Activator.CreateInstance(this.SerializedType); + } + + try + { + Type expectedType = this.SerializedType; + Type serializedType; + + if (reader.EnterNode(out serializedType)) + { + if (serializedType != expectedType) + { + if (serializedType != null) + { + context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.Name + " but the serialized value is of type " + serializedType.Name + "."); + + if (serializedType.IsCastableTo(expectedType)) + { + object value = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy).Deserialize(reader); + + bool serializedTypeIsNullable = serializedType.IsGenericType && serializedType.GetGenericTypeDefinition() == typeof(Nullable<>); + bool allowCastMethod = !this.IsNullable && !serializedTypeIsNullable; + + var castMethod = allowCastMethod ? serializedType.GetCastMethodDelegate(expectedType) : null; + + if (castMethod != null) + { + return castMethod(value); + } + else + { + return value; + } + } + else if (this.AllowDeserializeInvalidData || reader.Context.Config.AllowDeserializeInvalidData) + { + context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'."); + return GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); + } + else + { + context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + return Activator.CreateInstance(this.SerializedType); + } + } + else if (this.AllowDeserializeInvalidData || reader.Context.Config.AllowDeserializeInvalidData) + { + context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.GetNiceFullName() + " but the serialized type could not be resolved. Attempting to deserialize with possibly invalid data. Value may be lost or corrupted for node '" + name + "'."); + return GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); + } + else + { + context.Config.DebugContext.LogWarning("Expected complex struct value " + expectedType.Name + " but the serialized type could not be resolved. Value lost for node '" + name + "'."); + return Activator.CreateInstance(this.SerializedType); + } + } + else + { + return GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); + } + } + else + { + context.Config.DebugContext.LogError("Failed to enter node '" + name + "'."); + return Activator.CreateInstance(this.SerializedType); + } + } + catch (SerializationAbortException ex) + { + exitNode = false; + throw ex; + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + return Activator.CreateInstance(this.SerializedType); + } + finally + { + if (exitNode) + { + reader.ExitNode(); + } + } + } + else + { + switch (entry) + { + case EntryType.Null: + { + reader.ReadNull(); + return null; + } + + case EntryType.ExternalReferenceByIndex: + { + int index; + reader.ReadExternalReference(out index); + + object value = context.GetExternalObject(index); + + if (!object.ReferenceEquals(value, null) && !this.SerializedType.IsAssignableFrom(value.GetType())) + { + context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + this.SerializedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + return null; + } + + return value; + } + + case EntryType.ExternalReferenceByGuid: + { + Guid guid; + reader.ReadExternalReference(out guid); + + object value = context.GetExternalObject(guid); + + if (!object.ReferenceEquals(value, null) && !this.SerializedType.IsAssignableFrom(value.GetType())) + { + context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + this.SerializedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + return null; + } + + return value; + } + + case EntryType.ExternalReferenceByString: + { + string id; + reader.ReadExternalReference(out id); + + object value = context.GetExternalObject(id); + + if (!object.ReferenceEquals(value, null) && !this.SerializedType.IsAssignableFrom(value.GetType())) + { + context.Config.DebugContext.LogWarning("Can't cast external reference type " + value.GetType().GetNiceFullName() + " into expected type " + this.SerializedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + return null; + } + + return value; + } + + case EntryType.InternalReference: + { + int id; + reader.ReadInternalReference(out id); + + object value = context.GetInternalReference(id); + + if (!object.ReferenceEquals(value, null) && !this.SerializedType.IsAssignableFrom(value.GetType())) + { + context.Config.DebugContext.LogWarning("Can't cast internal reference type " + value.GetType().GetNiceFullName() + " into expected type " + this.SerializedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + return null; + } + + return value; + } + + case EntryType.StartOfNode: + { + try + { + Type expectedType = this.SerializedType; + Type serializedType; + int id; + + if (reader.EnterNode(out serializedType)) + { + id = reader.CurrentNodeId; + + object result; + + if (serializedType != null && expectedType != serializedType) // We have type metadata different from the expected type + { + bool success = false; + var isPrimitive = FormatterUtilities.IsPrimitiveType(serializedType); + + bool assignableCast; + + if (this.MayBeBoxedValueType && isPrimitive) + { + // It's a boxed primitive type, so simply read that straight and register success + var serializer = Serializer.Get(serializedType); + result = serializer.ReadValueWeak(reader); + success = true; + } + else if ((assignableCast = expectedType.IsAssignableFrom(serializedType)) || serializedType.HasCastDefined(expectedType, false)) + { + try + { + object value; + + if (isPrimitive) + { + var serializer = Serializer.Get(serializedType); + value = serializer.ReadValueWeak(reader); + } + else + { + var alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy); + value = alternateFormatter.Deserialize(reader); + } + + if (assignableCast) + { + result = value; + } + else + { + var castMethod = serializedType.GetCastMethodDelegate(expectedType); + + if (castMethod != null) + { + result = castMethod(value); + } + else + { + // Let's just give it a go anyways + result = value; + } + } + + success = true; + } + catch (SerializationAbortException ex) + { + exitNode = false; + throw ex; + } + catch (InvalidCastException) + { + success = false; + result = null; + } + } + else if (!this.IsAbstract && (this.AllowDeserializeInvalidData || reader.Context.Config.AllowDeserializeInvalidData)) + { + // We will try to deserialize an instance of T with the invalid data. + context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Attempting to deserialize with invalid data. Value may be lost or corrupted for node '" + name + "'."); + result = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); + success = true; + } + else + { + // We couldn't cast or use the type, but we still have to deserialize it and register + // the reference so the reference isn't lost if it is referred to further down + // the data stream. + + var alternateFormatter = FormatterLocator.GetFormatter(serializedType, context.Config.SerializationPolicy); + object value = alternateFormatter.Deserialize(reader); + + if (id >= 0) + { + context.RegisterInternalReference(id, value); + } + + result = null; + } + + if (!success) + { + // We can't use this + context.Config.DebugContext.LogWarning("Can't cast serialized type " + serializedType.GetNiceFullName() + " into expected type " + expectedType.GetNiceFullName() + ". Value lost for node '" + name + "'."); + result = null; + } + } + else if (this.IsAbstract) + { + result = null; + } + else + { + result = GetBaseFormatter(context.Config.SerializationPolicy).Deserialize(reader); + } + + if (id >= 0) + { + context.RegisterInternalReference(id, result); + } + + return result; + } + else + { + context.Config.DebugContext.LogError("Failed to enter node '" + name + "'."); + return null; + } + } + catch (SerializationAbortException ex) + { + exitNode = false; + throw ex; + } + catch (Exception ex) + { + context.Config.DebugContext.LogException(ex); + return null; + } + finally + { + if (exitNode) + { + reader.ExitNode(); + } + } + } + + // + // The below cases are for when we expect an object, but have + // serialized a straight primitive type. In such cases, we can + // often box the primitive type as an object. + // + // Sadly, the exact primitive type might be lost in case of + // integer and floating points numbers, as we don't know what + // type to expect. + // + // To be safe, we read and box the most precise type available. + // + + case EntryType.Boolean: + { + if (!this.MayBeBoxedValueType) + { + goto default; + } + + bool value; + reader.ReadBoolean(out value); + return value; + } + + case EntryType.FloatingPoint: + { + if (!this.MayBeBoxedValueType) + { + goto default; + } + + double value; + reader.ReadDouble(out value); + return value; + } + + case EntryType.Integer: + { + if (!this.MayBeBoxedValueType) + { + goto default; + } + + long value; + reader.ReadInt64(out value); + return value; + } + + case EntryType.String: + { + if (!this.MayBeBoxedValueType) + { + goto default; + } + + string value; + reader.ReadString(out value); + return value; + } + + case EntryType.Guid: + { + if (!this.MayBeBoxedValueType) + { + goto default; + } + + Guid value; + reader.ReadGuid(out value); + return value; + } + + default: + + // Lost value somehow + context.Config.DebugContext.LogWarning("Unexpected entry of type " + entry.ToString() + ", when a reference or node start was expected. A value has been lost."); + reader.SkipEntry(); + return null; + } + } + } + } + + public override void WriteValueWeak(string name, object value, IDataWriter writer) + { + if (this.IsEnum) + { + // Copied from EnumSerializer.cs + ulong ul; + + FireOnSerializedType(this.SerializedType); + + try + { + ul = Convert.ToUInt64(value as Enum); + } + catch (OverflowException) + { + unchecked + { + ul = (ulong)Convert.ToInt64(value as Enum); + } + } + + writer.WriteUInt64(name, ul); + } + else + { + // Copied from ComplexTypeSerializer.cs + var context = writer.Context; + var policy = context.Config.SerializationPolicy; + + if (policy.AllowNonSerializableTypes == false && this.SerializedType.IsSerializable == false) + { + context.Config.DebugContext.LogError("The type " + this.SerializedType.Name + " is not marked as serializable."); + return; + } + + FireOnSerializedType(this.SerializedType); + + if (this.IsValueType) + { + bool endNode = true; + + try + { + writer.BeginStructNode(name, this.SerializedType); + GetBaseFormatter(policy).Serialize(value, writer); + } + catch (SerializationAbortException ex) + { + endNode = false; + throw ex; + } + finally + { + if (endNode) + { + writer.EndNode(name); + } + } + } + else + { + int id; + int index; + string strId; + Guid guid; + + bool endNode = true; + + if (object.ReferenceEquals(value, null)) + { + writer.WriteNull(name); + } + else if (context.TryRegisterExternalReference(value, out index)) + { + writer.WriteExternalReference(name, index); + } + else if (context.TryRegisterExternalReference(value, out guid)) + { + writer.WriteExternalReference(name, guid); + } + else if (context.TryRegisterExternalReference(value, out strId)) + { + writer.WriteExternalReference(name, strId); + } + else if (context.TryRegisterInternalReference(value, out id)) + { + // Get type of actual stored object + // + // Don't have it as a strongly typed T value, since people can "override" (shadow) + // GetType() on derived classes with the "new" operator. By referencing the type + // as a System.Object, we ensure the correct GetType() method is always called. + // + // (Yes, this has actually happened, and this was done to fix it.) + + Type type = (value as object).GetType(); + + if (this.MayBeBoxedValueType && FormatterUtilities.IsPrimitiveType(type)) + // It's a boxed primitive type + { + try + { + writer.BeginReferenceNode(name, type, id); + + var serializer = Serializer.Get(type); + serializer.WriteValueWeak(value, writer); + } + catch (SerializationAbortException ex) + { + endNode = false; + throw ex; + } + finally + { + if (endNode) + { + writer.EndNode(name); + } + } + } + else + { + IFormatter formatter; + + if (object.ReferenceEquals(type, this.SerializedType)) + { + formatter = GetBaseFormatter(policy); + } + else + { + formatter = FormatterLocator.GetFormatter(type, policy); + } + + try + { + writer.BeginReferenceNode(name, type, id); + formatter.Serialize(value, writer); + } + catch (SerializationAbortException ex) + { + endNode = false; + throw ex; + } + finally + { + if (endNode) + { + writer.EndNode(name); + } + } + } + } + else + { + writer.WriteInternalReference(name, id); + } + } + } + } + + private IFormatter GetBaseFormatter(ISerializationPolicy serializationPolicy) + { + // This is an optimization - it's a lot cheaper to compare three references and do a null check, + // than it is to look something up in a dictionary. By far most of the time, we will be using + // one of these three policies. + + if (object.ReferenceEquals(serializationPolicy, UnityPolicy)) + { + if (this.UnityPolicyFormatter == null) + { + this.UnityPolicyFormatter = FormatterLocator.GetFormatter(this.SerializedType, UnityPolicy); + } + + return this.UnityPolicyFormatter; + } + else if (object.ReferenceEquals(serializationPolicy, EverythingPolicy)) + { + if (this.EverythingPolicyFormatter == null) + { + this.EverythingPolicyFormatter = FormatterLocator.GetFormatter(this.SerializedType, EverythingPolicy); + } + + return this.EverythingPolicyFormatter; + } + else if (object.ReferenceEquals(serializationPolicy, StrictPolicy)) + { + if (this.StrictPolicyFormatter == null) + { + this.StrictPolicyFormatter = FormatterLocator.GetFormatter(this.SerializedType, StrictPolicy); + } + + return this.StrictPolicyFormatter; + } + + IFormatter formatter; + + lock (this.FormattersByPolicy_LOCK) + { + if (!this.FormattersByPolicy.TryGetValue(serializationPolicy, out formatter)) + { + formatter = FormatterLocator.GetFormatter(this.SerializedType, serializationPolicy); + this.FormattersByPolicy.Add(serializationPolicy, formatter); + } + } + + return formatter; + } + } +} \ No newline at end of file diff --git a/DataSerializer/Item.cs.meta b/OdinSerializer/Core/Serializers/AnySerializer.cs.meta similarity index 83% rename from DataSerializer/Item.cs.meta rename to OdinSerializer/Core/Serializers/AnySerializer.cs.meta index 0186ed7..e832bde 100644 --- a/DataSerializer/Item.cs.meta +++ b/OdinSerializer/Core/Serializers/AnySerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: af731d0d0bc42774bb585b50388899b2 +guid: c6bed9136de1d41404e2805076a6123e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/BooleanSerializer.cs.meta b/OdinSerializer/Core/Serializers/BooleanSerializer.cs.meta index 0a056f1..1437580 100644 --- a/OdinSerializer/Core/Serializers/BooleanSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/BooleanSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b5d02d9d9efcf1e70f42cabb042199b4 +guid: da54a32f46cb6a94807727bfad26d03c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/ByteSerializer.cs.meta b/OdinSerializer/Core/Serializers/ByteSerializer.cs.meta index cc4809f..0346e5e 100644 --- a/OdinSerializer/Core/Serializers/ByteSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/ByteSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 56dd455551bfd04bc6bbaf3fb30fe90d +guid: 14c459857207efb7bff9144d42a34aa7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/CharSerializer.cs.meta b/OdinSerializer/Core/Serializers/CharSerializer.cs.meta index 736db46..5dfde99 100644 --- a/OdinSerializer/Core/Serializers/CharSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/CharSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e6bf5bd1890429c5b418fce01bd5f168 +guid: d4b6cc142a5092cd1572e0f8b6295255 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs b/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs index b80d51b..4f42015 100644 --- a/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs +++ b/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs @@ -27,7 +27,7 @@ namespace ToolBox.Serialization.OdinSerializer /// /// The type which the can serialize and deserialize. /// - public sealed class ComplexTypeSerializer : Serializer + public class ComplexTypeSerializer : Serializer { private static readonly bool ComplexTypeMayBeBoxedValueType = typeof(T).IsInterface || typeof(T) == typeof(object) || typeof(T) == typeof(ValueType) || typeof(T) == typeof(Enum); private static readonly bool ComplexTypeIsAbstract = typeof(T).IsAbstract || typeof(T).IsInterface; @@ -596,7 +596,15 @@ public override void WriteValue(string name, T value, IDataWriter writer) } else if (context.TryRegisterInternalReference(value, out id)) { - Type type = value.GetType(); // Get type of actual stored object + // Get type of actual stored object + // + // Don't have it as a strongly typed T value, since people can "override" (shadow) + // GetType() on derived classes with the "new" operator. By referencing the type + // as a System.Object, we ensure the correct GetType() method is always called. + // + // (Yes, this has actually happened, and this was done to fix it.) + + Type type = (value as object).GetType(); if (ComplexTypeMayBeBoxedValueType && FormatterUtilities.IsPrimitiveType(type)) // It's a boxed primitive type diff --git a/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs.meta b/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs.meta index da8a7e8..db7b9a3 100644 --- a/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/ComplexTypeSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 961bee2ea0d9de24188e7dc9cea4c2ce +guid: de41099f7e2e7102b4b8ff914f4a5e1d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/DecimalSerializer.cs.meta b/OdinSerializer/Core/Serializers/DecimalSerializer.cs.meta index 4c45c42..da1145b 100644 --- a/OdinSerializer/Core/Serializers/DecimalSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/DecimalSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dae352b1463fa361f99650e818ddc34d +guid: 0ac4bd8acd129554a336cfdb43be88b0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/DoubleSerializer.cs.meta b/OdinSerializer/Core/Serializers/DoubleSerializer.cs.meta index e2a234f..99eb1b7 100644 --- a/OdinSerializer/Core/Serializers/DoubleSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/DoubleSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 71b93a0066a37a68b24971e15add3807 +guid: c824250f4d18529b60f17da851ee5765 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/EnumSerializer.cs.meta b/OdinSerializer/Core/Serializers/EnumSerializer.cs.meta index 420b3a8..13295be 100644 --- a/OdinSerializer/Core/Serializers/EnumSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/EnumSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f35b17f5be4f2051e10c6d33c6319e59 +guid: e8aa735637b359809ded6300cd57b273 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/GuidSerializer.cs.meta b/OdinSerializer/Core/Serializers/GuidSerializer.cs.meta index a5b1a16..6cd9826 100644 --- a/OdinSerializer/Core/Serializers/GuidSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/GuidSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 273ac54badc06bbe1786e04b972ec35e +guid: 6e14e8e30890c80a18a2607fc624ebf3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/Int16Serializer.cs.meta b/OdinSerializer/Core/Serializers/Int16Serializer.cs.meta index cb0f3b5..8bafc6d 100644 --- a/OdinSerializer/Core/Serializers/Int16Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/Int16Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0fb664366a0f8451ce9737414b306956 +guid: d23f096fae05e8120df6a1a4309e0b89 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/Int32Serializer.cs.meta b/OdinSerializer/Core/Serializers/Int32Serializer.cs.meta index 1bd0083..5b7f9ca 100644 --- a/OdinSerializer/Core/Serializers/Int32Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/Int32Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e325a00418a3e958d67821965b9236b +guid: 478dfe3520d49952a739723535283079 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/Int64Serializer.cs.meta b/OdinSerializer/Core/Serializers/Int64Serializer.cs.meta index 4c334ab..f627479 100644 --- a/OdinSerializer/Core/Serializers/Int64Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/Int64Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 313af3f16a250c96dfd5e1b0cbfaf6aa +guid: 4a97a4504f17d76bbe5554325d1fd04e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/IntPtrSerializer.cs.meta b/OdinSerializer/Core/Serializers/IntPtrSerializer.cs.meta index eebfa1a..2bfa7ed 100644 --- a/OdinSerializer/Core/Serializers/IntPtrSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/IntPtrSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 801192ca1691b6aa47291198ef55e1d1 +guid: 0152dd2d52ac583a208a1fcc8eb6c9cd MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/SByteSerializer.cs.meta b/OdinSerializer/Core/Serializers/SByteSerializer.cs.meta index 4ea2224..aaab2b5 100644 --- a/OdinSerializer/Core/Serializers/SByteSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/SByteSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5d0efdc2b9033bfca5ddd79ee36e88fa +guid: a4710e0c6fbd02229ab534bff39ec8fb MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/Serializer.cs b/OdinSerializer/Core/Serializers/Serializer.cs index 2feaacb..1f404c3 100644 --- a/OdinSerializer/Core/Serializers/Serializer.cs +++ b/OdinSerializer/Core/Serializers/Serializer.cs @@ -24,6 +24,11 @@ namespace ToolBox.Serialization.OdinSerializer using System.Diagnostics; using System.Reflection; + +// System.ExecutionEngineException is marked obsolete in .NET 4.6 +// That's all very good for .NET, but Unity still uses it! +#pragma warning disable 618 + /// /// Serializes and deserializes a given type, and wraps serialization and deserialization with all the proper calls to free formatters from tedious boilerplate. /// @@ -58,7 +63,8 @@ public abstract class Serializer private static readonly object LOCK = new object(); - private static readonly Dictionary ReaderWriterCache = new Dictionary(FastTypeComparer.Instance); + private static readonly Dictionary Weak_ReaderWriterCache = new Dictionary(FastTypeComparer.Instance); + private static readonly Dictionary Strong_ReaderWriterCache = new Dictionary(FastTypeComparer.Instance); #if UNITY_EDITOR @@ -107,7 +113,7 @@ public static Serializer GetForValue(object value) /// A for type T. public static Serializer Get() { - return (Serializer)Serializer.Get(typeof(T)); + return (Serializer)Serializer.Get(typeof(T), false); } /// @@ -117,6 +123,11 @@ public static Serializer Get() /// A for the given type. /// The type argument is null. public static Serializer Get(Type type) + { + return Get(type, true); + } + + private static Serializer Get(Type type, bool allowWeakFallback) { if (type == null) { @@ -125,12 +136,14 @@ public static Serializer Get(Type type) Serializer result; + var cache = allowWeakFallback ? Weak_ReaderWriterCache : Strong_ReaderWriterCache; + lock (LOCK) { - if (ReaderWriterCache.TryGetValue(type, out result) == false) + if (cache.TryGetValue(type, out result) == false) { - result = Create(type); - ReaderWriterCache.Add(type, result); + result = Create(type, allowWeakFallback); + cache.Add(type, result); } } @@ -162,14 +175,21 @@ public void WriteValueWeak(object value, IDataWriter writer) /// The writer to use. public abstract void WriteValueWeak(string name, object value, IDataWriter writer); - private static Serializer Create(Type type) + private static Serializer Create(Type type, bool allowWeakfallback) { + ExecutionEngineException aotEx = null; + try { Type resultType = null; if (type.IsEnum) { + if (allowWeakfallback && !EmitUtilities.CanEmit) + { + return new AnySerializer(type); + } + resultType = typeof(EnumSerializer<>).MakeGenericType(type); } else if (FormatterUtilities.IsPrimitiveType(type)) @@ -185,20 +205,21 @@ private static Serializer Create(Type type) } else { + if (allowWeakfallback && !EmitUtilities.CanEmit) + { + return new AnySerializer(type); + } + resultType = typeof(ComplexTypeSerializer<>).MakeGenericType(type); } return (Serializer)Activator.CreateInstance(resultType); } - // System.ExecutionEngineException is marked obsolete in .NET 4.6 - // That's all very good for .NET, but Unity still uses it! -#pragma warning disable 618 catch (TargetInvocationException ex) { if (ex.GetBaseException() is ExecutionEngineException) { - LogAOTError(type, ex.GetBaseException() as ExecutionEngineException); - return null; + aotEx = ex.GetBaseException() as ExecutionEngineException; } else { @@ -209,8 +230,7 @@ private static Serializer Create(Type type) { if (ex.GetBaseException() is ExecutionEngineException) { - LogAOTError(type, ex.GetBaseException() as ExecutionEngineException); - return null; + aotEx = ex.GetBaseException() as ExecutionEngineException; } else { @@ -219,9 +239,16 @@ private static Serializer Create(Type type) } catch (ExecutionEngineException ex) { - LogAOTError(type, ex); - return null; + aotEx = ex; } + + if (allowWeakfallback) + { + return new AnySerializer(type); + } + + LogAOTError(type, aotEx); + throw aotEx; } private static void LogAOTError(Type type, ExecutionEngineException ex) @@ -235,8 +262,6 @@ private static void LogAOTError(Type type, ExecutionEngineException ex) } } -#pragma warning restore 618 - /// /// Serializes and deserializes the type , and wraps serialization and deserialization with all the proper calls to free formatters from tedious boilerplate. /// diff --git a/OdinSerializer/Core/Serializers/Serializer.cs.meta b/OdinSerializer/Core/Serializers/Serializer.cs.meta index 169f572..f8d3f05 100644 --- a/OdinSerializer/Core/Serializers/Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 213283c36dbd874d9d17428718f13af0 +guid: 65177bbbab19830232e62349c0a4f264 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/SingleSerializer.cs.meta b/OdinSerializer/Core/Serializers/SingleSerializer.cs.meta index 13839b4..1d7096d 100644 --- a/OdinSerializer/Core/Serializers/SingleSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/SingleSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: dc7011a8a5a6e8ea5ec52b27552643ac +guid: b198138f3178994bb001be2054f741d3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/StringSerializer.cs.meta b/OdinSerializer/Core/Serializers/StringSerializer.cs.meta index 9afbd48..a8494f0 100644 --- a/OdinSerializer/Core/Serializers/StringSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/StringSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 245dd7eca5239ef0641f3180a5833846 +guid: 609395ff1250db4f6e26dfa27316bcf6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/UInt16Serializer.cs.meta b/OdinSerializer/Core/Serializers/UInt16Serializer.cs.meta index b416a3b..0a53fc4 100644 --- a/OdinSerializer/Core/Serializers/UInt16Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/UInt16Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e05f59f09e5c0c621c271d50eebb2836 +guid: a000b0f2af1b880edf2eeac0077d432c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/UInt32Serializer.cs.meta b/OdinSerializer/Core/Serializers/UInt32Serializer.cs.meta index f7ff722..9615f5e 100644 --- a/OdinSerializer/Core/Serializers/UInt32Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/UInt32Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bb3fd3e423d1ede967458ba39ac8aeed +guid: dc76558a33bcad24ed7ee3e22783acab MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/UInt64Serializer.cs.meta b/OdinSerializer/Core/Serializers/UInt64Serializer.cs.meta index 5dc2993..6596bb9 100644 --- a/OdinSerializer/Core/Serializers/UInt64Serializer.cs.meta +++ b/OdinSerializer/Core/Serializers/UInt64Serializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d33a7768e116860aea86e0f42eb528e9 +guid: efd4c8dcc2006088b5ca44d1ded160a3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs b/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs index 67626fd..f3fb9f4 100644 --- a/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs +++ b/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs @@ -1,5 +1,5 @@ //----------------------------------------------------------------------- -// +// // Copyright (c) 2018 Sirenix IVS // // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs.meta b/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs.meta index 2c0d8cd..a31ee9f 100644 --- a/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs.meta +++ b/OdinSerializer/Core/Serializers/UIntPtrSerializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 99edd4cbff9ca83fc6d9a858220e186e +guid: 90ad7cb69e9f2a9a216e7aafed746fc4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/LICENSE.meta b/OdinSerializer/LICENSE.meta index 333fd32..95718d1 100644 --- a/OdinSerializer/LICENSE.meta +++ b/OdinSerializer/LICENSE.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 7fd2f9a5772bfd2a3c63dbe2a4517cbe +guid: e33861c0a72b1abb7ce6222ea5e11c84 TextScriptImporter: userData: assetBundleName: diff --git a/OdinSerializer/ToolBox.Serialization.OdinSerializer.asmdef.meta b/OdinSerializer/ToolBox.Serialization.OdinSerializer.asmdef.meta index 5b6fc8f..5e3543f 100644 --- a/OdinSerializer/ToolBox.Serialization.OdinSerializer.asmdef.meta +++ b/OdinSerializer/ToolBox.Serialization.OdinSerializer.asmdef.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 40f914846e8079065ce748d2b233be6f +guid: 7d491a4487b0aa01ed511252ac3c660f folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Unity Integration.meta b/OdinSerializer/Unity Integration.meta index ab31304..689aae2 100644 --- a/OdinSerializer/Unity Integration.meta +++ b/OdinSerializer/Unity Integration.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f47aaba154427ed46b5e5b318b26306e +guid: 359f07f841517fe40a411a54bec3a570 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Unity Integration/AOTSupportScanner.cs b/OdinSerializer/Unity Integration/AOTSupportScanner.cs index 12f196c..8f01128 100644 --- a/OdinSerializer/Unity Integration/AOTSupportScanner.cs +++ b/OdinSerializer/Unity Integration/AOTSupportScanner.cs @@ -30,12 +30,15 @@ namespace ToolBox.Serialization.OdinSerializer.Editor using System.Reflection; using UnityEngine.SceneManagement; using System.Collections; + using UnityEditorInternal; public sealed class AOTSupportScanner : IDisposable { private bool scanning; private bool allowRegisteringScannedTypes; private HashSet seenSerializedTypes = new HashSet(); + private HashSet scannedPathsNoDependencies = new HashSet(); + private HashSet scannedPathsWithDependencies = new HashSet(); private static System.Diagnostics.Stopwatch smartProgressBarWatch = System.Diagnostics.Stopwatch.StartNew(); private static int smartProgressBarDisplaysSinceLastUpdate = 0; @@ -49,6 +52,8 @@ public void BeginScan() allowRegisteringScannedTypes = false; this.seenSerializedTypes.Clear(); + this.scannedPathsNoDependencies.Clear(); + this.scannedPathsWithDependencies.Clear(); FormatterLocator.OnLocatedEmittableFormatterForType += this.OnLocatedEmitType; FormatterLocator.OnLocatedFormatter += this.OnLocatedFormatter; @@ -197,7 +202,6 @@ public bool ScanAllAddressables(bool includeAssetDependencies, bool showProgress 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; @@ -226,7 +230,13 @@ public bool ScanAllAddressables(bool includeAssetDependencies, bool showProgress if (AddressableAssetGroup_GatherAllAssets == null) throw new NotSupportedException("AddressableAssetGroup.GatherAllAssets(List results, bool includeSelf, bool recurseAll, bool includeSubObjects, Func entryFilter) method not found"); } - bool hasPlayerDataGroupSchema = (bool)AddressableAssetGroup_HasSchema.Invoke(group, new object[] { PlayerDataGroupSchema_Type }); + bool hasPlayerDataGroupSchema = false; + + if (PlayerDataGroupSchema_Type != null) + { + 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); @@ -542,6 +552,20 @@ public bool ScanScenes(string[] scenePaths, bool includeSceneDependencies, bool public bool ScanAsset(string assetPath, bool includeAssetDependencies) { + if (includeAssetDependencies) + { + if (this.scannedPathsWithDependencies.Contains(assetPath)) return true; // Already scanned this asset + + this.scannedPathsWithDependencies.Add(assetPath); + this.scannedPathsNoDependencies.Add(assetPath); + } + else + { + if (this.scannedPathsNoDependencies.Contains(assetPath)) return true; // Already scanned this asset + + this.scannedPathsNoDependencies.Add(assetPath); + } + if (assetPath.EndsWith(".unity")) { return this.ScanScenes(new string[] { assetPath }, includeAssetDependencies, false); @@ -549,7 +573,7 @@ public bool ScanAsset(string assetPath, bool includeAssetDependencies) if (!(assetPath.EndsWith(".asset") || assetPath.EndsWith(".prefab"))) { - // ScanAsset can only scan .asset and .prefab assets. + // ScanAsset can only scan .unity, .asset and .prefab assets. return false; } @@ -615,9 +639,15 @@ public List EndScan() { if (!this.scanning) throw new InvalidOperationException("Cannot end a scan when scanning has not begun."); - var result = this.seenSerializedTypes.ToList(); + var results = new HashSet(); + + foreach (var type in this.seenSerializedTypes) + { + GatherValidAOTSupportTypes(type, results); + } + this.Dispose(); - return result; + return results.ToList(); } public void Dispose() @@ -636,28 +666,24 @@ public void Dispose() private void OnLocatedEmitType(Type type) { - if (!AllowRegisterType(type)) return; - - this.RegisterType(type); + if (!this.allowRegisteringScannedTypes) return; + this.seenSerializedTypes.Add(type); } private void OnSerializedType(Type type) - { - if (!AllowRegisterType(type)) return; - - this.RegisterType(type); - } + { + if (!this.allowRegisteringScannedTypes) return; + this.seenSerializedTypes.Add(type); + } private void OnLocatedFormatter(IFormatter formatter) { var type = formatter.SerializedType; + if (type == null || !this.allowRegisteringScannedTypes) return; + this.seenSerializedTypes.Add(type); + } - if (type == null) return; - if (!AllowRegisterType(type)) return; - this.RegisterType(type); - } - - private static bool AllowRegisterType(Type type) + public static bool AllowRegisterType(Type type) { if (IsEditorOnlyAssembly(type.Assembly)) return false; @@ -675,9 +701,157 @@ private static bool AllowRegisterType(Type type) private static bool IsEditorOnlyAssembly(Assembly assembly) { - return EditorAssemblyNames.Contains(assembly.GetName().Name); + if (EditorAssemblyNames.Contains(assembly.GetName().Name)) + { + return true; + } + + bool result; + if (!IsEditorOnlyAssembly_Cache.TryGetValue(assembly, out result)) + { + try + { + var name = assembly.GetName().Name; + string[] guids = AssetDatabase.FindAssets(name); + string[] paths = new string[guids.Length]; + + int dllCount = 0; + int dllIndex = 0; + + for (int i = 0; i < guids.Length; i++) + { + paths[i] = AssetDatabase.GUIDToAssetPath(guids[i]); + if (paths[i].EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || paths[i].EndsWith(".asmdef", StringComparison.OrdinalIgnoreCase)) + { + dllCount++; + dllIndex = i; + } + } + + if (dllCount == 1) + { + var path = paths[dllIndex]; + var assetImporter = AssetImporter.GetAtPath(path); + + if (assetImporter is PluginImporter) + { + var pluginImporter = assetImporter as PluginImporter; + + if (!pluginImporter.GetCompatibleWithEditor()) + { + result = false; + } + else if (pluginImporter.DefineConstraints.Any(n => n == "UNITY_EDITOR")) + { + result = true; + } + else + { + bool isCompatibleWithAnyNonEditorPlatform = false; + + foreach (var member in typeof(BuildTarget).GetFields(BindingFlags.Public | BindingFlags.Static)) + { + BuildTarget platform = (BuildTarget)member.GetValue(null); + + int asInt = Convert.ToInt32(platform); + + if (member.IsDefined(typeof(ObsoleteAttribute)) || asInt < 0) + continue; + + if (pluginImporter.GetCompatibleWithPlatform(platform)) + { + isCompatibleWithAnyNonEditorPlatform = true; + break; + } + } + + result = !isCompatibleWithAnyNonEditorPlatform; + } + } + else if (assetImporter is AssemblyDefinitionImporter) + { + var asmDefImporter = assetImporter as AssemblyDefinitionImporter; + var asset = AssetDatabase.LoadAssetAtPath(path); + + if (asset == null) + { + result = false; + goto HasResult; + } + + var data = JsonUtility.FromJson(asset.text); + + if (data != null) + { + if (data.defineConstraints != null) + { + for (int i = 0; i < data.defineConstraints.Length; i++) + { + if (data.defineConstraints[i].Trim() == "UNITY_EDITOR") + { + result = true; + goto HasResult; + } + } + } + } + + result = false; + } + else + { + result = false; + } + } + else + { + // There's either 0 or multiple of them; either way, we guess it will + // be included in the build, so we return false, it's probably not + // an editor only assembly. + result = false; + } + + HasResult: + + IsEditorOnlyAssembly_Cache.Add(assembly, result); + } + catch (Exception ex) + { + Debug.LogException(ex); + IsEditorOnlyAssembly_Cache[assembly] = false; + } + } + + return result; + } + + [Serializable] + class VersionDefine + { + public string name; + public string expression; + public string define; } + [Serializable] + class AssemblyDefinitionData + { + public string name; + public string rootNamespace; + public string[] references; + public string[] includePlatforms; + public string[] excludePlatforms; + public bool allowUnsafeCode; + public bool overrideReferences; + public string[] precompiledReferences; + public bool autoReferenced; + public string[] defineConstraints; + public VersionDefine[] versionDefines; + public bool noEngineReferences; + } + + private static Dictionary IsEditorOnlyAssembly_Cache = new Dictionary(); + private static HashSet EditorAssemblyNames = new HashSet() { "Assembly-CSharp-Editor", @@ -686,27 +860,25 @@ private static bool IsEditorOnlyAssembly(Assembly assembly) "Assembly-CSharp-Editor-firstpass", "Assembly-UnityScript-Editor-firstpass", "Assembly-Boo-Editor-firstpass", + "Sirenix.OdinInspector.Editor", + "Sirenix.Utilities.Editor", + "Sirenix.Reflection.Editor", typeof(Editor).Assembly.GetName().Name }; - private void RegisterType(Type type) + private static void GatherValidAOTSupportTypes(Type type, HashSet results) { - if (!this.allowRegisteringScannedTypes) return; - //if (type.IsAbstract || type.IsInterface) return; if (type.IsGenericType && (type.IsGenericTypeDefinition || !type.IsFullyConstructedGenericType())) return; + if (!AllowRegisterType(type)) return; - //if (this.seenSerializedTypes.Add(type)) - //{ - // Debug.Log("Added " + type.GetNiceFullName()); - //} - - this.seenSerializedTypes.Add(type); - - if (type.IsGenericType) + if (results.Add(type)) { - foreach (var arg in type.GetGenericArguments()) + if (type.IsGenericType) { - this.RegisterType(arg); + foreach (var arg in type.GetGenericArguments()) + { + GatherValidAOTSupportTypes(arg, results); + } } } } diff --git a/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta b/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta index 049e7f0..85f8eec 100644 --- a/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta +++ b/OdinSerializer/Unity Integration/AOTSupportScanner.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c8d2f558c4f427b6f1ec5a8a9b128746 +guid: 3b5751cd2cace309d31c36f4cf8381b1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/AOTSupportUtilities.cs b/OdinSerializer/Unity Integration/AOTSupportUtilities.cs index 646f969..926c4ce 100644 --- a/OdinSerializer/Unity Integration/AOTSupportUtilities.cs +++ b/OdinSerializer/Unity Integration/AOTSupportUtilities.cs @@ -105,7 +105,7 @@ public static void GenerateDLL(string dirPath, string assemblyName, List s assembly.SetCustomAttribute(new CustomAttributeBuilder(typeof(EmittedAssemblyAttribute).GetConstructor(new Type[0]), new object[0])); - // The following is a fix for Unity's crappy Mono runtime that doesn't know how to do this sort + // The following is a fix for Unity's 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, @@ -158,8 +158,31 @@ public static void GenerateDLL(string dirPath, string assemblyName, List s supportSerializedTypes.Add(typeof(DateTimeFormatter)); } - //var endPoint = il.DefineLabel(); - //il.Emit(OpCodes.Br, endPoint); + // Now we aggressively figure out extra types to support that this type will probably need + { + var allTypesToSupport = new HashSet(supportSerializedTypes); + + // Look at members and static serializer fields in all formatters to find types to support + foreach (var typeToSupport in supportSerializedTypes) + { + RecursivelyAddExtraTypesToSupport(typeToSupport, allTypesToSupport); + } + + // Supplement with a hard-coded search for static serializer fields in all defined weak formatters, + // as the above will often not find those. + foreach (var loadedAssembly in AppDomain.CurrentDomain.GetAssemblies()) + { + foreach (var loadedType in loadedAssembly.SafeGetTypes()) + { + if (!loadedType.IsAbstract && typeof(WeakBaseFormatter).IsAssignableFrom(loadedType)) + { + GatherExtraTypesToSupportFromStaticFormatterFields(loadedType, allTypesToSupport); + } + } + } + + supportSerializedTypes = allTypesToSupport.ToList(); + } foreach (var serializedType in supportSerializedTypes) { @@ -231,6 +254,17 @@ public static void GenerateDLL(string dirPath, string assemblyName, List s il.Emit(OpCodes.Newobj, formatterConstructor); il.Emit(OpCodes.Pop); } + else + { + formatterConstructor = formatter.GetType().GetConstructor(new Type[] { typeof(Type) }); + + if (formatterConstructor != null) + { + il.Emit(OpCodes.Ldnull); + il.Emit(OpCodes.Newobj, formatterConstructor); + il.Emit(OpCodes.Pop); + } + } } //// Make sure we have a proper reflection formatter variant if all else goes wrong @@ -379,6 +413,57 @@ public static void GenerateDLL(string dirPath, string assemblyName, List s AssetDatabase.SaveAssets(); } + + private static void RecursivelyAddExtraTypesToSupport(Type typeToSupport, HashSet allTypesToSupport) + { + if (FormatterUtilities.IsPrimitiveType(typeToSupport)) return; + + var serializedMembers = FormatterUtilities.GetSerializableMembers(typeToSupport, SerializationPolicies.Unity); + + // Gather all members that would normally be serialized in this type + foreach (var member in serializedMembers) + { + var memberType = member.GetReturnType(); + + if (!AOTSupportScanner.AllowRegisterType(memberType)) continue; + + if (allTypesToSupport.Add(memberType)) + { + RecursivelyAddExtraTypesToSupport(memberType, allTypesToSupport); + } + } + + if (typeof(UnityEngine.Object).IsAssignableFrom(typeToSupport)) return; + + // Gather all types referenced by static serializer references + var formatters = FormatterLocator.GetAllCompatiblePredefinedFormatters(typeToSupport, SerializationPolicies.Unity); + + foreach (var formatter in formatters) + { + GatherExtraTypesToSupportFromStaticFormatterFields(formatter.GetType(), allTypesToSupport); + } + } + + private static void GatherExtraTypesToSupportFromStaticFormatterFields(Type formatterType, HashSet allTypesToSupport) + { + var staticFormatterFields = formatterType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly | BindingFlags.Static); + + foreach (var field in staticFormatterFields) + { + var parameters = field.FieldType.GetArgumentsOfInheritedOpenGenericClass(typeof(Serializer<>)); + + if (parameters != null) + { + foreach (var parameterType in parameters) + { + if (allTypesToSupport.Add(parameterType)) + { + RecursivelyAddExtraTypesToSupport(parameterType, allTypesToSupport); + } + } + } + } + } } } diff --git a/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta b/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta index fec46a2..3111d73 100644 --- a/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta +++ b/OdinSerializer/Unity Integration/AOTSupportUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3a3f807790d5e0d216a45974c6457927 +guid: 10a7f775c3097c63ad191f00fee90081 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport.meta index 9339d41..f9fffd5 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 07829c19e6b89f84d9f5469fa695b5b0 +guid: 5aec1b81ad6ff0646ba4cf79052b96b3 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta index 67fcbd5..f8816ec 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/BaseDictionaryKeyPathProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: be91985d929303aaa07d702164171450 +guid: 207f2c5a18fd2d80e5b51153a05dba11 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta index 1dd7c13..c55bc6c 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/DictionaryKeyUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 09bac25a652e6e8493ec547f9580785e +guid: 427cadbc2df6eb140316955a4a6771be MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta index 11a12e5..6a753c5 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/IDictionaryKeyPathProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 871791495b35c898bc011d5994bd3b5a +guid: 8cd85f7704cc5ef7fbca96516a58ff4b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta index 9d2c2fb..835eaa1 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/RegisterDictionaryKeyPathProviderAttribute.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a3319ebb429af99e734f6b81ee8e25f1 +guid: 4e57d433fafc1696dcb4305764d5bdd0 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta index eb2af99..3837e51 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector2DictionaryKeyPathProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6857d261faa06d25f16731611a9d1cb8 +guid: 5e6cb2beb47f62cf7a7fa7033b4ceda4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta index bab45ab..5569b31 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector3DictionaryKeyPathProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 93e6d81c30682515c172b7e0d2feaf55 +guid: 0337ebeee1d7362a39ada17a5c89d4d1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta index 77df6cf..dc4a73c 100644 --- a/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta +++ b/OdinSerializer/Unity Integration/DictionaryKeySupport/Vector4DictionaryKeyPathProvider.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c7320b56c6554472b052779c582a881d +guid: 0b365bf9bfdbbb5739ad07b85686c04f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters.meta b/OdinSerializer/Unity Integration/Formatters.meta index 74a1765..d5d242d 100644 --- a/OdinSerializer/Unity Integration/Formatters.meta +++ b/OdinSerializer/Unity Integration/Formatters.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3cbe2125d39cac642a7a80773f2dd272 +guid: d8b79577d32e516458c57445ad17cf7d folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta index 2c1a57a..bc49f1a 100644 --- a/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/AnimationCurveFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 92e89808c1417ce6bb730a9d0288bf45 +guid: 0c277d82db3e2a94f9460496be976c9f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta index a1ec0a7..4972186 100644 --- a/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/BoundsFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a49f68546b9e9eb48c59af276220de76 +guid: b48cbb7cde86285b1d5a2462c6151eee MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta index 7b00466..f0b7b62 100644 --- a/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/Color32Formatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: ba65bb8ab90fe55d615eb0c477841ecc +guid: 8abbc7c3f0f6aa320d871cbf6f8937fd MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs b/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs index dc05b57..d9dc343 100644 --- a/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs +++ b/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs @@ -28,12 +28,25 @@ namespace ToolBox.Serialization.OdinSerializer public class ColorBlockFormatterLocator : IFormatterLocator { - public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, out IFormatter formatter) + public bool TryGetFormatter(Type type, FormatterLocationStep step, ISerializationPolicy policy, bool allowWeakFallbackFormatters, out IFormatter formatter) { if (step == FormatterLocationStep.BeforeRegisteredFormatters && type.FullName == "UnityEngine.UI.ColorBlock") { - var formatterType = typeof(ColorBlockFormatter<>).MakeGenericType(type); - formatter = (IFormatter)Activator.CreateInstance(formatterType); + try + { + formatter = (IFormatter)Activator.CreateInstance(typeof(ColorBlockFormatter<>).MakeGenericType(type)); + } + catch (Exception ex) + { +#pragma warning disable CS0618 // Type or member is obsolete + if (allowWeakFallbackFormatters && (ex is ExecutionEngineException || ex.GetBaseException() is ExecutionEngineException)) +#pragma warning restore CS0618 // Type or member is obsolete + { + formatter = new WeakColorBlockFormatter(type); + } + else throw; + } + return true; } @@ -92,4 +105,48 @@ protected override void Write(ref T value, IDataWriter writer) FloatSerializer.WriteValue((float)fadeDuration.GetValue(value, null), writer); } } + + public class WeakColorBlockFormatter : WeakBaseFormatter + { + private static readonly Serializer FloatSerializer = Serializer.Get(); + private static readonly Serializer ColorSerializer = Serializer.Get(); + + private readonly PropertyInfo normalColor; + private readonly PropertyInfo highlightedColor; + private readonly PropertyInfo pressedColor; + private readonly PropertyInfo disabledColor; + private readonly PropertyInfo colorMultiplier; + private readonly PropertyInfo fadeDuration; + + public WeakColorBlockFormatter(Type colorBlockType) + : base(colorBlockType) + { + normalColor = colorBlockType.GetProperty("normalColor"); + highlightedColor = colorBlockType.GetProperty("highlightedColor"); + pressedColor = colorBlockType.GetProperty("pressedColor"); + disabledColor = colorBlockType.GetProperty("disabledColor"); + colorMultiplier = colorBlockType.GetProperty("colorMultiplier"); + fadeDuration = colorBlockType.GetProperty("fadeDuration"); + } + + protected override void DeserializeImplementation(ref object value, IDataReader reader) + { + normalColor.SetValue(value, ColorSerializer.ReadValue(reader), null); + highlightedColor.SetValue(value, ColorSerializer.ReadValue(reader), null); + pressedColor.SetValue(value, ColorSerializer.ReadValue(reader), null); + disabledColor.SetValue(value, ColorSerializer.ReadValue(reader), null); + colorMultiplier.SetValue(value, FloatSerializer.ReadValue(reader), null); + fadeDuration.SetValue(value, FloatSerializer.ReadValue(reader), null); + } + + protected override void SerializeImplementation(ref object 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/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta index b5b555f..3f9a9bc 100644 --- a/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/ColorBlockFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2ef0b3eb852e13a4b1825488fdff68a7 +guid: 1378b87301b623527a63d358bd5efa1b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta index a8c74f6..990df95 100644 --- a/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/ColorFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c27ef99825f2ffb385ccfe78a2cf768e +guid: d019aa43f92e57e2dd7fd5c3df53a313 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta index 8f0d216..5139e24 100644 --- a/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/CoroutineFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b89f226b593fc401b9a68d73d6315267 +guid: afdb8299eaea9910b3fc94197e4952dc MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta index bc05def..089eb92 100644 --- a/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/GradientAlphaKeyFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b43e76a31931992aeea48af761367626 +guid: 9587eca0e82f897b2abf22fc40cb1b53 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta index da0cfc5..ea8477b 100644 --- a/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/GradientColorKeyFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c14c805b65279eff74f332c5b8c1efc1 +guid: 5145e6db343b1e8e8a573d046b2b9903 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta index fcecce6..b5696f6 100644 --- a/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/GradientFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0d56153f638e46b2f4a03890ddfeceaf +guid: dfc4a1481540e307e2cbbb97d990fe1a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta index 9a51250..e31e5bf 100644 --- a/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/KeyframeFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fc4068534500d7b82609335f6a103dfa +guid: b68c1697295aa5d1f76e15070221a31e MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta index b51a494..52a2101 100644 --- a/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/LayerMaskFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 6289ae6796ee01cafffb237dc3ccbb59 +guid: f56031c384ded2f9ff85885b66146788 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta index 587037d..c21bf75 100644 --- a/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/QuaternionFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 64ca84482f289b29f497d7840d1ac9ce +guid: 90777e1a519dd5fe64c46a0798f7b468 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta index 8d8fe20..91e579b 100644 --- a/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/RectFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a3d7e5f7f34ff385466c6320431f5b8e +guid: 72168b0ed2e29ca3c1daaa6e964e81d7 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs b/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs index d084267..580ce53 100644 --- a/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs +++ b/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs @@ -18,10 +18,11 @@ using ToolBox.Serialization.OdinSerializer; -[assembly: RegisterFormatter(typeof(UnityEventFormatter<>))] +[assembly: RegisterFormatter(typeof(UnityEventFormatter<>), weakFallback: typeof(WeakUnityEventFormatter))] namespace ToolBox.Serialization.OdinSerializer { + using System; using UnityEngine.Events; /// @@ -42,4 +43,16 @@ protected override T GetUninitializedObject() return new T(); } } + + public class WeakUnityEventFormatter : WeakReflectionFormatter + { + public WeakUnityEventFormatter(Type serializedType) : base(serializedType) + { + } + + protected override object GetUninitializedObject() + { + return Activator.CreateInstance(this.SerializedType); + } + } } \ No newline at end of file diff --git a/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta index 204b32f..2744e49 100644 --- a/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/UnityEventFormatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f02c0e23359f1191f074944af759b11b +guid: f27ad5e4ade7272dc06837c01e86767f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta index b5d1c6b..e0408f9 100644 --- a/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/Vector2Formatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2357d32337de5648b46a5224ce3efb53 +guid: efd10f5305244f939f44885ab187a749 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta index d45f537..b7c92f5 100644 --- a/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/Vector3Formatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 148f505e0c3eef14cee3264818c98b22 +guid: 0f334ffedc877c28d6833250d82bacc9 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta b/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta index b70d94a..37f5e8f 100644 --- a/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta +++ b/OdinSerializer/Unity Integration/Formatters/Vector4Formatter.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4208f4a794998daa97a072c541fa8f32 +guid: d60e1c548ce1b3e3606d9487e9d843ad MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs b/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs new file mode 100644 index 0000000..152f046 --- /dev/null +++ b/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------- +// +// 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. +// +//----------------------------------------------------------------------- + +using ToolBox.Serialization.OdinSerializer; + +[assembly: RegisterFormatter(typeof(Vector2IntFormatter))] +[assembly: RegisterFormatter(typeof(Vector3IntFormatter))] + +namespace ToolBox.Serialization.OdinSerializer +{ + using UnityEngine; + + /// + /// Custom formatter for the type. + /// + /// + public class Vector2IntFormatter : MinimalBaseFormatter + { + private static readonly Serializer Serializer = OdinSerializer.Serializer.Get(); + + /// + /// Reads into the specified value using the specified reader. + /// + /// The value to read into. + /// The reader to use. + protected override void Read(ref Vector2Int value, IDataReader reader) + { + value.x = Vector2IntFormatter.Serializer.ReadValue(reader); + value.y = Vector2IntFormatter.Serializer.ReadValue(reader); + } + + /// + /// Writes from the specified value using the specified writer. + /// + /// The value to write from. + /// The writer to use. + protected override void Write(ref Vector2Int value, IDataWriter writer) + { + Vector2IntFormatter.Serializer.WriteValue(value.x, writer); + Vector2IntFormatter.Serializer.WriteValue(value.y, writer); + } + } + + /// + /// Custom formatter for the type. + /// + /// + public class Vector3IntFormatter : MinimalBaseFormatter + { + private static readonly Serializer Serializer = OdinSerializer.Serializer.Get(); + + /// + /// Reads into the specified value using the specified reader. + /// + /// The value to read into. + /// The reader to use. + protected override void Read(ref Vector3Int value, IDataReader reader) + { + value.x = Vector3IntFormatter.Serializer.ReadValue(reader); + value.y = Vector3IntFormatter.Serializer.ReadValue(reader); + value.z = Vector3IntFormatter.Serializer.ReadValue(reader); + } + + /// + /// Writes from the specified value using the specified writer. + /// + /// The value to write from. + /// The writer to use. + protected override void Write(ref Vector3Int value, IDataWriter writer) + { + Vector3IntFormatter.Serializer.WriteValue(value.x, writer); + Vector3IntFormatter.Serializer.WriteValue(value.y, writer); + Vector3IntFormatter.Serializer.WriteValue(value.z, writer); + } + } +} \ No newline at end of file diff --git a/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs.meta b/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs.meta new file mode 100644 index 0000000..932d82a --- /dev/null +++ b/OdinSerializer/Unity Integration/Formatters/VectorIntFormatters.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e5e74fd92dc5c333477f424d781e45ce +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta b/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta index 2feee74..e53a144 100644 --- a/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta +++ b/OdinSerializer/Unity Integration/OdinPrefabSerializationEditorUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9acd4e890b6b35c30c3fd61265331cee +guid: 9915924b7b5626acc071ff7b07f1699d MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects.meta index f595cce..3a48037 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fc4bddf9bb38f7a43b58b0928fa04fe5 +guid: f3c4d207b4cc00b4489ce81ce584b03b folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs index 96dc2bd..8b4017a 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs @@ -15,6 +15,7 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { /// diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta index 03e0a3a..c750b38 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationFormat.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: afc953f9e87a3f9cd41c03fcc2c63f63 +guid: d37dc2065185fcadd6c6e72a21e001e9 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta index 974b2e9..6f9a598 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/IOverridesSerializationPolicy.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 94fffcb1ec3682964748aa2a85b8555b +guid: c54b1ef6379381291203c400e524d66c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs index 4c98afb..ad6aeb3 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs @@ -15,6 +15,7 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { /// diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta index a1ddd2c..f6c4936 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/ISupportsPrefabSerialization.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 178d516eb71b504a00fd9429867d2e73 +guid: dd3cb3c67dd21d861a2a2c023264770f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs index 80ba036..a7daddc 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs @@ -15,6 +15,7 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { using System; diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta index ae20261..d9659e2 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializationData.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0fa1876ead256ac7538983bc70dea1d5 +guid: be36d5233767bfb4dc04af70fd98a024 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs index 3ec0326..f2236bc 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs @@ -15,8 +15,10 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -35,12 +37,14 @@ public abstract class SerializedBehaviour : Behaviour, ISerializationCallbackRec void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -59,4 +63,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta index 9d3a936..e4bbf4a 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedBehaviour.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2968f44f461aa3a0e7ce2ad7c12b1ae1 +guid: a902b5479461951c2dd460ab7c1e45aa MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs index 2f5d55a..8e6fa91 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs @@ -15,8 +15,10 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -35,12 +37,14 @@ public abstract class SerializedComponent : Component, ISerializationCallbackRec void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -59,4 +63,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta index ead96c6..30ad960 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedComponent.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: bfdb7184099fce496154075e2094ba30 +guid: 5e55ba2b5c6bf139d05d4bd70aa8741c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs index db7bd18..f684037 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs @@ -18,6 +18,7 @@ namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -36,12 +37,14 @@ public abstract class SerializedMonoBehaviour : MonoBehaviour, ISerializationCal void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -60,4 +63,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta index a513818..77b8470 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedMonoBehaviour.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e7b777b001cb1f1d823d5402efc8e19f +guid: 5c70e9eb0defaeba6b4221271db404e8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs index f2b7a0e..b74cb81 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs @@ -15,8 +15,10 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -33,12 +35,14 @@ public abstract class SerializedScriptableObject : ScriptableObject, ISerializat void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -57,4 +61,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta index 1c99799..41040a1 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedScriptableObject.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 67e3a85def6ed5e29f9588fdfa2141cb +guid: 52b61c977de101896bbde74245ed47f4 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs index a5b1d29..62b416a 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs @@ -15,8 +15,10 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -33,12 +35,14 @@ public abstract class SerializedStateMachineBehaviour : StateMachineBehaviour, I void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -57,4 +61,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta index a8a7c4e..151c8d1 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedStateMachineBehaviour.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5f0a1afce7b0f21e48b5745d2b5f6c24 +guid: ad868b60e77e7ea762d39bdf8471bc06 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs index 8e7f804..6e5528a 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs @@ -15,8 +15,10 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer { + using Utilities; using UnityEngine; /// @@ -33,12 +35,14 @@ public abstract class SerializedUnityObject : UnityEngine.Object, ISerialization void ISerializationCallbackReceiver.OnAfterDeserialize() { + if (this.SafeIsUnityNull()) return; UnitySerializationUtility.DeserializeUnityObject(this, ref this.serializationData); this.OnAfterDeserialize(); } void ISerializationCallbackReceiver.OnBeforeSerialize() { + if (this.SafeIsUnityNull()) return; this.OnBeforeSerialize(); UnitySerializationUtility.SerializeUnityObject(this, ref this.serializationData); } @@ -57,4 +61,4 @@ protected virtual void OnBeforeSerialize() { } } -} \ No newline at end of file +} diff --git a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta index 3e136d4..047765a 100644 --- a/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta +++ b/OdinSerializer/Unity Integration/SerializedUnityObjects/SerializedUnityObject.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e8449c02db374b5a18edfff47ee4d7db +guid: c4b3784dde228015b173d7da56888677 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta b/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta index d1fa6fc..9885468 100644 --- a/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta +++ b/OdinSerializer/Unity Integration/UnityReferenceResolver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: b4ffa07ccd4937532874e2c6f26fa6b2 +guid: 8345d50ae57a08af570895e3fd4f5015 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta b/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta index 86ffc6e..a5e82e9 100644 --- a/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta +++ b/OdinSerializer/Unity Integration/UnitySerializationInitializer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d5e45197853bb72d1571d2c1be6a61ca +guid: 646a7647ac5654a8aeffb7b46374aab6 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Unity Integration/UnitySerializationUtility.cs b/OdinSerializer/Unity Integration/UnitySerializationUtility.cs index 266a447..5e59027 100644 --- a/OdinSerializer/Unity Integration/UnitySerializationUtility.cs +++ b/OdinSerializer/Unity Integration/UnitySerializationUtility.cs @@ -1825,18 +1825,19 @@ public static void DeserializeUnityObject(UnityEngine.Object unityObject, IDataR 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"; + "Experience shows that this issue is particularly likely to occur on prefab instances, and if this is the case, the parent prefab is also under suspicion, and should be re-saved and re-imported. " + + "Note that 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\". " + + message += "A delayed error 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(); + var log = "DELAYED SERIALIZATION LOG: Name = " + (unityObject != null ? unityObject.name : "(DESTROYED UNITY OBJECT)") + ", Type = " + unityObject.GetType().GetNiceFullName(); UnityEngine.Object toPing = unityObject; @@ -1860,7 +1861,7 @@ public static void DeserializeUnityObject(UnityEngine.Object unityObject, IDataR UnityEditor.AssetDatabase.SaveAssets(); } - Debug.LogWarning(log, toPing); + Debug.LogError(log, toPing); }; } catch diff --git a/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta b/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta index 51e5d2e..7d2f968 100644 --- a/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta +++ b/OdinSerializer/Unity Integration/UnitySerializationUtility.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 3dfe10677ce8dba368cc0b392f722b31 +guid: 5c67d4ad54fb899a46ac65a7ebd7dd23 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities.meta b/OdinSerializer/Utilities.meta index c183862..1d78169 100644 --- a/OdinSerializer/Utilities.meta +++ b/OdinSerializer/Utilities.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e604f074ac8b3b54daa4f885e7f5a30d +guid: f8fb31d82544f7f4da1998f77db559dc folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Utilities/Extensions.meta b/OdinSerializer/Utilities/Extensions.meta index cf5b2cf..e00502b 100644 --- a/OdinSerializer/Utilities/Extensions.meta +++ b/OdinSerializer/Utilities/Extensions.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0e764ce7bf888ec45ae4dffb928a6b80 +guid: 887ef752faa32de45837fbedf8dd69de folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta index 1f61baa..3733181 100644 --- a/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/FieldInfoExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 624e32ee019951692565398ca84793d1 +guid: 35e0254df47241911568af2b882251b8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta b/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta index a613b6a..38fce92 100644 --- a/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta +++ b/OdinSerializer/Utilities/Extensions/GarbageFreeIterators.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 4c0ce785f0da7bac7860c6423d569bb0 +guid: be8d4602f6556f5477ddf03fea35b95f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta index e7f8b3b..98e6db8 100644 --- a/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/LinqExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7ba5070a912fbed113802dad67f92842 +guid: 53381e2fc3d388f7d3b568166e16de59 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs b/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs index 2c5f21e..9b0b91c 100644 --- a/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs +++ b/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs @@ -15,6 +15,7 @@ // limitations under the License. // //----------------------------------------------------------------------- + namespace ToolBox.Serialization.OdinSerializer.Utilities { using System; @@ -191,7 +192,7 @@ public static bool IsStatic(this MemberInfo member) string message = string.Format( CultureInfo.InvariantCulture, "Unable to determine IsStatic for member {0}.{1}" + - "MemberType was {2} but only fields, properties and methods are supported.", + "MemberType was {2} but only fields, properties, methods, events and types are supported.", member.DeclaringType.FullName, member.Name, member.GetType().FullName); diff --git a/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta index a6d92aa..81118be 100644 --- a/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/MemberInfoExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7cb509867be75b6cd3c5de341ba2a603 +guid: fdacfa38bedb2e0b782b787dca1a811a MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta index e369d04..7a8d66a 100644 --- a/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/MethodInfoExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 573b7a5063a1a4e65f677fbf062d9022 +guid: 84918fd8b8136c9c8c0c84994e6f4025 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/Operator.cs.meta b/OdinSerializer/Utilities/Extensions/Operator.cs.meta index 043b7be..4b43ba0 100644 --- a/OdinSerializer/Utilities/Extensions/Operator.cs.meta +++ b/OdinSerializer/Utilities/Extensions/Operator.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 91f3981b5b3bb122d9e8fa4ad98b75f4 +guid: 47c8ac44e7de5f669290b54426387666 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta b/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta index 655fee2..dfeda1c 100644 --- a/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta +++ b/OdinSerializer/Utilities/Extensions/PathUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 0c24e3b62d4eeb25f71fb4a6ba1e9dab +guid: 6333b03c28057467649c5c52b270d5e5 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta index b96a4c3..6ce20eb 100644 --- a/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/PropertyInfoExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8dfff584aaa9b9a49cfec1c1bb758200 +guid: 4a9f731b2eb55d08c8b1870cc93e00e2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta index d5462c5..d30b482 100644 --- a/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/StringExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 7af3c8bc9ce6399095ff815fbe38bf09 +guid: f39c383c6497275741ba968b778ef234 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta index 763a043..8f879ec 100644 --- a/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/TypeExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a21975075f424c04dca6e0ecab065c62 +guid: 850bb6de5223451f8e56f15666cda3e2 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta b/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta index 0f4f469..2c17b54 100644 --- a/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta +++ b/OdinSerializer/Utilities/Extensions/UnityExtensions.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: cd67121a0ada2ea3383d995ab88f776d +guid: 459a608ab951c3654dc4e69e935bc59c MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc.meta b/OdinSerializer/Utilities/Misc.meta index a6bc04e..ace8d08 100644 --- a/OdinSerializer/Utilities/Misc.meta +++ b/OdinSerializer/Utilities/Misc.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8464808ade87a1748ab1b30e4a87057d +guid: 94755b96e1e83ed4bb48f47cca77d438 folderAsset: yes DefaultImporter: externalObjects: {} diff --git a/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs b/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs index 7b531ed..e32bb87 100644 --- a/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs +++ b/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs @@ -35,20 +35,23 @@ public enum OdinAssemblyImportSettings /// Include the assembly in the build, but not in the editor. /// IncludeInBuildOnly, + /// /// Include the assembly in the editor, but not in the build. /// IncludeInEditorOnly, + /// /// Include the assembly in both the build and in the editor. /// IncludeInAll, + /// /// Exclude the assembly from both the build and from the editor. /// ExcludeFromAll, } - + /// /// Utility for correctly setting import on OdinSerializer assemblies based on platform and scripting backend. /// @@ -62,7 +65,7 @@ public static class AssemblyImportSettingsUtilities /// /// All valid Unity BuildTarget platforms. /// - public static readonly ImmutableList Platforms; + public static readonly ImmutableList Platforms; /// /// All valid Unity BuildTarget platforms that support Just In Time compilation. @@ -103,9 +106,7 @@ static AssemblyImportSettingsUtilities() { BuildTarget.StandaloneWindows, BuildTarget.StandaloneWindows64, - BuildTarget.StandaloneLinux, BuildTarget.StandaloneLinux64, - BuildTarget.StandaloneLinuxUniversal, BuildTarget.Android }) .ToArray()); @@ -185,7 +186,7 @@ public static void SetAssemblyImportSettings(BuildTarget platform, string assemb throw new InvalidOperationException("Failed to get PluginImporter for " + assemblyFilePath); } - bool updateImportSettings = + bool updateImportSettings = importer.GetCompatibleWithAnyPlatform() // If the 'any platform' flag is true, then reapply settings no matter what to ensure that everything is correct. //|| Platforms.Any(p => importer.GetCompatibleWithPlatform(p) != includeInBuild) || importer.GetCompatibleWithPlatform(platform) != includeInBuild @@ -210,7 +211,7 @@ public static void SetAssemblyImportSettings(BuildTarget platform, string assemb public static ScriptingImplementation GetCurrentScriptingBackend() { var buildGroup = EditorUserBuildSettings.selectedBuildTargetGroup; - + if (getScriptingBackendMethod != null) { return (ScriptingImplementation)getScriptingBackendMethod.Invoke(null, new object[] { buildGroup }); diff --git a/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta b/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta index c051787..5244f27 100644 --- a/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta +++ b/OdinSerializer/Utilities/Misc/AssemblyImportSettingsUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 790e81e366cc661cd4d7a4f9a7db24a4 +guid: b91947669b972db8f4d91b504bb82b05 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/Cache.cs.meta b/OdinSerializer/Utilities/Misc/Cache.cs.meta index f0fe0bd..6019563 100644 --- a/OdinSerializer/Utilities/Misc/Cache.cs.meta +++ b/OdinSerializer/Utilities/Misc/Cache.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 1139509772fbddf47a3e81d1795a408c +guid: 0078f8577ba94a8af1bdce1456a668b3 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta b/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta index a921fe4..cadf63d 100644 --- a/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta +++ b/OdinSerializer/Utilities/Misc/DoubleLookupDictionary.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 49ac35a4ed5d21f0e00727d3e3824bd3 +guid: d406f92ffec20f2eea481b11bf825607 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta b/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta index 5594b39..15738f4 100644 --- a/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta +++ b/OdinSerializer/Utilities/Misc/EmitUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8113566cf08cabb12041541a7470f990 +guid: 3917ec891378872a63b9cf9babfda304 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta b/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta index 6e4417e..42ad866 100644 --- a/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta +++ b/OdinSerializer/Utilities/Misc/FastTypeComparer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: fa100ef4e4c1f2d343413c173de8fd97 +guid: 958c2f46d963de55b2ae1c3af0a24dee MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/Flags.cs.meta b/OdinSerializer/Utilities/Misc/Flags.cs.meta index 2e58f3b..f90bf95 100644 --- a/OdinSerializer/Utilities/Misc/Flags.cs.meta +++ b/OdinSerializer/Utilities/Misc/Flags.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a902eb61719b1be807ca999b5ca5eb99 +guid: 54f74503682271ea3b922fbf9db45d83 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta b/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta index aebd365..0bcc845 100644 --- a/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta +++ b/OdinSerializer/Utilities/Misc/ICacheNotificationReceiver.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: f0bcce7fbbe449b68c996bee46c3c11a +guid: e98ac6b7f0392df9a7f50d2159ce0495 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta b/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta index c0b6761..8e4f5c7 100644 --- a/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta +++ b/OdinSerializer/Utilities/Misc/ImmutableList.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 462a3b7c859d39073a0f9f4b4afab64a +guid: ebd83d9289193294722949f51761c7e1 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta b/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta index 7e8a654..afecd9f 100644 --- a/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta +++ b/OdinSerializer/Utilities/Misc/MemberAliasFieldInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 5355287304dda3da45ba25cabb3e9ef4 +guid: 918063c733db1010fa841c350b66ae05 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta b/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta index 0c70bf9..3d3cc61 100644 --- a/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta +++ b/OdinSerializer/Utilities/Misc/MemberAliasMethodInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 9e98ff155905323fcda931db050ef9c9 +guid: 9bd1665fc7add2c658ccb500a0d8472f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta b/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta index 8dabc17..4a3e60c 100644 --- a/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta +++ b/OdinSerializer/Utilities/Misc/MemberAliasPropertyInfo.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: c93d08d6352ae8746321fb70b3553d78 +guid: bda9b4b0e9eec6f1d2aebb5bfd68ac7f MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta b/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta index 3f18c29..4405320 100644 --- a/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta +++ b/OdinSerializer/Utilities/Misc/ReferenceEqualityComparer.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 40fb769e654cf014d91f6a9f47c5fb11 +guid: 0653045b6d3837e5b20174811ab368a8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta b/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta index db2e326..0ef708d 100644 --- a/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta +++ b/OdinSerializer/Utilities/Misc/UnityVersion.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: a73584e21b1818391c302ef8ec0a3ce5 +guid: 403585623b2616763be9cbdc95619e84 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs b/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs index 4987a01..e0b9af7 100644 --- a/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs +++ b/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs @@ -129,7 +129,7 @@ public static unsafe string StringFromBytes(byte[] buffer, int charLength, bool } GCHandle toHandle = default(GCHandle); - string result = new string(default(char), charLength); // Creaty empty string of required length + string result = new string(' ', charLength); // Creaty empty string of required length try { diff --git a/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta b/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta index 83fdaed..1bf09bf 100644 --- a/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta +++ b/OdinSerializer/Utilities/Misc/UnsafeUtilities.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 2769f49e1b1d9c3075a073c1f9dd2b87 +guid: 78dcf301a7fbf12e89200193d449e5b8 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/OdinSerializer/Version.txt b/OdinSerializer/Version.txt index 232b94f..a888770 100644 --- a/OdinSerializer/Version.txt +++ b/OdinSerializer/Version.txt @@ -1 +1 @@ -2021.4.9 - 1e36f3c5 \ No newline at end of file +2024.2.27 - 3d90af7a \ No newline at end of file diff --git a/OdinSerializer/Version.txt.meta b/OdinSerializer/Version.txt.meta index 82c6cfe..02b1bde 100644 --- a/OdinSerializer/Version.txt.meta +++ b/OdinSerializer/Version.txt.meta @@ -1,6 +1,6 @@ fileFormatVersion: 2 -guid: 92d0987ceae764c88d5ba4ddaf7a3f6f +guid: 475ee844b07caf62f2e1cdc2cf58ce87 TextScriptImporter: userData: assetBundleName: diff --git a/README.md b/README.md index 4d31831..61e9100 100644 --- a/README.md +++ b/README.md @@ -147,15 +147,17 @@ public struct SaveData 3. Press the ```Load assets at paths``` button. -4. That's all! +4. Repeat step 3 every time you create a new asset. -![image](https://user-images.githubusercontent.com/53948684/117006947-776b6980-ad02-11eb-997c-e9108e5c3f97.png) +![image](https://github.com/IntoTheDev/Save-System-for-Unity/assets/53948684/10e575a2-a4f6-4693-98c3-1e04dca618ec) ### AOT platforms -You need to create a simple C# class and implement ```ITypeProvider``` interface. Then you need to add types (except primitive ones) that will be saved in your game. +AOT (IL2CPP) works without any additional work, but IF some types do NOT serialize, please follow the steps below. -Example for case above +You need to create a simple C# class that implements the ```ITypeProvider``` interface. Then, you need to define the types (excluding primitive ones) that fail to save for some reason. + +Although it should work without an ITypeProvider, for the sake of simplicity, I'll use the case above as an example. ```csharp using System; diff --git a/package.json b/package.json index c50aa30..3be3f43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "com.intothedev.savesystem", - "version": "1.7.0", + "version": "1.8.0", "displayName": "Save System", "description": "Save System with Odin Serializer.", "author": {