private static string[] CreateNotes(MemberInfo member, SerializationFlags flags, SerializationBackendFlags serializationBackend, string serializationPolicyId) { List <string> notes = new List <string>(); StringBuilder buffer = new StringBuilder(); // Handle the default serialization policy. if (serializationBackend.HasNone(SerializationBackendFlags.Odin) || flags.HasAll(SerializationFlags.DefaultSerializationPolicy)) { // Member type if (flags.HasAll(SerializationFlags.Property | SerializationFlags.AutoProperty)) { buffer.AppendFormat("The auto property {0} ", member.GetNiceName()); } else if (flags.HasAll(SerializationFlags.Property)) { buffer.AppendFormat("The non-auto property {0} ", member.GetNiceName()); } else { if (flags.HasAll(SerializationFlags.Public)) { buffer.AppendFormat("The public field {0} ", member.GetNiceName()); } else { buffer.AppendFormat("The field {0} ", member.GetNiceName()); } } // Is the member serialized? if (flags.HasAny(SerializationFlags.SerializedByOdin | SerializationFlags.SerializedByUnity)) { buffer.Append("is serialized by "); // Who? if (flags.HasAll(SerializationFlags.SerializedByUnity | SerializationFlags.SerializedByOdin)) { buffer.Append("both Unity and Odin "); } else if (flags.HasAll(SerializationFlags.SerializedByUnity)) { buffer.Append("Unity "); } else { buffer.Append("Odin "); } buffer.Append("because "); // Why? var relevant = flags & (SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.NonSerializedAttribute); if (flags.HasAll(SerializationFlags.OdinSerializeAttribute) && serializationBackend.HasAll(SerializationBackendFlags.Odin)) // The OdinSerialize attribute is only relevant when the Odin serialization backend is available. { relevant |= SerializationFlags.OdinSerializeAttribute; } switch (relevant) { case SerializationFlags.Public: buffer.Append("its access modifier is public. "); break; case SerializationFlags.SerializeFieldAttribute: buffer.Append("it the [SerializeField] attribute is defined. "); break; case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute: buffer.Append("it the [SerializeField] attribute is defined, and it's public. "); break; case SerializationFlags.OdinSerializeAttribute: buffer.Append("it the [OdinSerialize] attribute is defined. "); break; case SerializationFlags.Public | SerializationFlags.OdinSerializeAttribute: buffer.Append("it the [OdinSerialize] attribute is defined, and it's public."); break; case SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute: buffer.Append("it the [SerializeField] and [OdinSerialize] attributes are defined. "); break; case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute: buffer.Append("its access modifier is public and the [SerializeField] and [OdinSerialize] attribute are defined. "); break; case SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute: case SerializationFlags.Public | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute: case SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute: case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute | SerializationFlags.NonSerializedAttribute: buffer.Append("the [OdinSerialize] and [NonSerialized] attribute are defined. "); break; default: buffer.Append("(MISSING CASE: " + relevant.ToString() + ")"); break; } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Why is the value not serialized by Unity? if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && flags.HasNone(SerializationFlags.SerializedByUnity)) { buffer.Append("The member is not being serialized by Unity since "); if (flags.HasAll(SerializationFlags.Property)) { buffer.Append("Unity does not serialize properties."); } else if (UnitySerializationUtility.GuessIfUnityWillSerialize(member.GetReturnType()) == false) { buffer.Append("Unity does not support the type."); } else if (flags.HasAll(SerializationFlags.NonSerializedAttribute)) { buffer.Append("the [NonSerialized] attribute is defined."); } else if (flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute) == false) { buffer.Append("it is neither a public field or has the [SerializeField] attribute."); } else { buffer.Append("# Missing case, please report: " + flags.ToString()); } } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Why is the value not serialized by Odin? if (flags.HasAll(SerializationFlags.SerializedByOdin) == false) { buffer.Append("Member is not serialized by Odin because "); if ((serializationBackend & SerializationBackendFlags.Odin) != 0) { if (flags.HasAll(SerializationFlags.SerializedByUnity)) { buffer.Append("the member is already serialized by Unity. "); } } else { buffer.Append("Odin serialization is not implemented. "); if (flags.HasAll(SerializationFlags.OdinSerializeAttribute)) { buffer.Append("The use of [OdinSerialize] attribute is invalid."); } } } } else // Why not? { // Property members with Odin implementation. if (flags.HasAll(SerializationFlags.Property) && serializationBackend.HasAll(SerializationBackendFlags.Odin)) { if (flags.HasAll(SerializationFlags.AutoProperty) == false) { buffer.Append("is skipped by Odin because non-auto properties are not serialized. "); if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute)) { buffer.Append("The use of [SerializeField] and [OdinSerialize] attributes is invalid. "); } else if (flags.HasAll(SerializationFlags.OdinSerializeAttribute)) { buffer.Append("The use of [OdinSerialize] attribute is invalid. "); } else if (flags.HasAll(SerializationFlags.SerializeFieldAttribute)) { buffer.Append("The use of [SerializeField] attribute is invalid. "); } else if (flags.HasAll(SerializationFlags.NonSerializedAttribute)) { buffer.Append("The use of [NonSerialized] attribute is unnecessary. "); } } else { buffer.Append("Auto property member is skipped by Odin because "); if (flags.HasNone(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute)) { buffer.Append("neither [SerializeField] nor [OdinSerialize] attributes have been used."); } } } // Property members without Odin implementation. else if (flags.HasAll(SerializationFlags.Property)) { buffer.Append("is skipped by Unity because Unity does not serialize properties. "); if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute)) { buffer.Append("The use of [SerializeField] and [OdinSerialize] attributes is invalid. "); } else if (flags.HasAll(SerializationFlags.OdinSerializeAttribute)) { buffer.Append("The use of [OdinSerialize] attribute is invalid. "); } else if (flags.HasAny(SerializationFlags.SerializeFieldAttribute)) { buffer.Append("The use of [SerializeField] attribute is invalid. "); } if (flags.HasAny(SerializationFlags.NonSerializedAttribute)) { buffer.Append("The use of [NonSerialized] attribute is unnecessary."); } } // Field members. else { // Backend ? buffer.Append("is skipped by "); switch (serializationBackend) { case SerializationBackendFlags.Unity: buffer.Append("Unity "); break; case SerializationBackendFlags.Odin: buffer.Append("Odin "); break; case SerializationBackendFlags.UnityAndOdin: buffer.Append("both Unity and Odin "); break; } buffer.Append("because "); if (serializationBackend == SerializationBackendFlags.None) { buffer.Append("there is no serialization backend? "); } else if (flags.HasAll(SerializationFlags.NonSerializedAttribute)) { buffer.Append("the [NonSerialized] attribute is defined. "); } else if (flags.HasNone(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute)) { buffer.Append("the field is neither public nor is the [SerializeField] attribute defined. "); } else if (serializationBackend == SerializationBackendFlags.Unity && flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute)) { buffer.Append("Unity does not support the type " + member.GetReturnType().GetNiceName()); } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Invalid use of OdinSerialize. if ((serializationBackend & SerializationBackendFlags.Odin) == 0 && flags.HasAll(SerializationFlags.OdinSerializeAttribute)) { notes.Add("Odin serialization is not implemented. The use of [OdinSerialize] attribute is invalid."); // Just add this line directly to the notes list. } } // Using both [SerializeField] and [NonSerialized] attributes. if (flags.HasAll(SerializationFlags.SerializeFieldAttribute | SerializationFlags.NonSerializedAttribute) && flags.HasNone(SerializationFlags.OdinSerializeAttribute)) { notes.Add("Use of [SerializeField] along with [NonSerialized] attributes is weird. Remove either the [SerializeField] or [NonSerialized] attribute."); } } } else // Custom serialization policy. { if (flags.HasAll(SerializationFlags.AutoProperty)) { buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public auto property " : "The auto property "); } else if (flags.HasAll(SerializationFlags.Property)) { buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public property " : "The property "); } else { buffer.Append(flags.HasAll(SerializationFlags.Public) ? "The public field " : "The field "); } buffer.AppendFormat("{0} ", member.GetNiceName()); // Serialized by Unity. if (flags.HasAll(SerializationFlags.SerializedByUnity)) { buffer.Append("is serialized by Unity since "); if (flags.HasAll(SerializationFlags.Field)) { switch (flags & (SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute)) { case SerializationFlags.Public: buffer.Append("its access modifier is public. "); break; case SerializationFlags.SerializeFieldAttribute: buffer.Append("it the [SerializeField] attribute is defined. "); break; case SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute: buffer.Append("it the [SerializeField] attribute is defined, and it's public. "); break; default: buffer.Append("(MISSING CASE: " + flags.ToString() + ")"); break; } } else { buffer.Append("is serialized by Unity? Unity should not be serializing any properties? "); } } else { buffer.Append("is skipped by Unity "); if (flags.HasAll(SerializationFlags.Field)) { if (flags.HasNone(SerializationFlags.TypeSupportedByUnity)) { buffer.Append("because Unity does not support the type " + member.GetReturnType().GetNiceName()); } else if (flags.HasAll(SerializationFlags.NonSerializedAttribute)) { buffer.Append("because the [NonSerialized] attribute is defined. "); } else if (flags.HasNone(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute)) { buffer.Append("because the field is not public nor is the [SerializeField] attribute is not defined. "); } else { buffer.Append("(MISSING CASE: " + flags.ToString() + ")"); } } else { buffer.Append("because Unity does not serialize properties. "); } } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Serialized by Odin with custom serialization policy. if (flags.HasAll(SerializationFlags.AutoProperty)) { buffer.Append("The auto property "); } else if (flags.HasAll(SerializationFlags.Property)) { buffer.Append("The property "); } else { buffer.Append("The field "); } if (flags.HasAll(SerializationFlags.SerializedByOdin)) { buffer.Append("is serialized by Odin because of custom serialization policy: " + serializationPolicyId); } else { buffer.Append("is skipped by Odin because of custom serialization policy: " + serializationPolicyId); } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Serialized by both Unity and Odin. if (flags.HasAll(SerializationFlags.SerializedByOdin | SerializationFlags.SerializedByUnity)) { notes.Add("The member is serialized by both Unity and Odin. Consider ensuring that only one serializer is in use."); } } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Add notes on Unity serialization support. if (serializationBackend.HasAll(SerializationBackendFlags.UnityAndOdin)) { if (flags.HasAll(SerializationFlags.SerializedByOdin | SerializationFlags.TypeSupportedByUnity) && flags.HasNone(SerializationFlags.SerializedByUnity)) { buffer.Append("The type " + member.GetReturnType().GetNiceName() + " appears to be supported by Unity. Are you certain that you want to use Odin for serializing?"); } else if (flags.HasAll(SerializationFlags.SerializedByOdin) && flags.HasNone(SerializationFlags.TypeSupportedByUnity)) { buffer.Append("The type " + member.GetReturnType().GetNiceName() + " is not supported by Unity" + GuessWhyUnityDoesNotSupport(member.GetReturnType())); } } else if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && flags.HasNone(SerializationFlags.TypeSupportedByUnity)) { buffer.Append("The type " + member.GetReturnType().GetNiceName() + " is not supported by Unity" + GuessWhyUnityDoesNotSupport(member.GetReturnType())); } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Implement Odin support. if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && serializationBackend.HasNone(SerializationBackendFlags.Odin) && flags.HasNone(SerializationFlags.TypeSupportedByUnity) && flags.HasAny(SerializationFlags.Public | SerializationFlags.SerializeFieldAttribute | SerializationFlags.OdinSerializeAttribute) && flags.HasAny(SerializationFlags.Field | SerializationFlags.AutoProperty)) { string inheritFrom = "You could implement Odin serializing by inheriting " + member.DeclaringType.GetNiceName() + " from "; if (typeof(MonoBehaviour).IsAssignableFrom(member.DeclaringType)) { buffer.Append(inheritFrom + typeof(SerializedMonoBehaviour).GetNiceName()); } else if (UnityNetworkingUtility.NetworkBehaviourType != null && UnityNetworkingUtility.NetworkBehaviourType.IsAssignableFrom(member.DeclaringType)) { buffer.Append(inheritFrom + " SerializedNetworkBehaviour"); } else if (typeof(Behaviour).IsAssignableFrom(member.DeclaringType)) { buffer.Append(inheritFrom + typeof(SerializedBehaviour).GetNiceName()); } else if (typeof(ScriptableObject).IsAssignableFrom(member.DeclaringType)) { buffer.Append(inheritFrom + typeof(SerializedScriptableObject).GetNiceName()); } } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } // Recommend using fields instead of auto properties. if (serializationBackend.HasAll(SerializationBackendFlags.Odin) && flags.HasAll(SerializationFlags.AutoProperty | SerializationFlags.SerializedByOdin)) { buffer.Append("It's recommend to use backing fields for serializing instead of auto-properties."); } // Empty the buffer. if (buffer.Length > 0) { notes.Add(buffer.ToString()); buffer.Length = 0; } return(notes.ToArray()); }
private static MemberSerializationInfo CreateInfoFor(MemberInfo member, SerializationBackendFlags serializationBackend, bool serializeUnityFields, ISerializationPolicy serializationPolicy) { SerializationFlags flags = 0; // Is the member a field, property or auto-property? if (member is FieldInfo) { var f = member as FieldInfo; flags |= SerializationFlags.Field; if (f.IsPublic) { flags |= SerializationFlags.Public; } } else if (member is PropertyInfo) { var p = member as PropertyInfo; flags |= SerializationFlags.Property; if (p.GetGetMethod() != null && p.GetGetMethod().IsPublic || p.GetSetMethod() != null && p.GetSetMethod().IsPublic) { flags |= SerializationFlags.Public; } if (p.IsAutoProperty()) { flags |= SerializationFlags.AutoProperty; } } // Is the default serialization policy in use? if (serializationPolicy != null && serializationPolicy.ID == SerializationPolicies.Unity.ID) { flags |= SerializationFlags.DefaultSerializationPolicy; } // Will Unity serialize the member? if ((serializationBackend & SerializationBackendFlags.Unity) != 0 && UnitySerializationUtility.GuessIfUnityWillSerialize(member)) { flags |= SerializationFlags.SerializedByUnity; } // Will Odin serialize the member? if ((serializationBackend & SerializationBackendFlags.Odin) != 0 && UnitySerializationUtility.OdinWillSerialize(member, serializeUnityFields, serializationPolicy)) { flags |= SerializationFlags.SerializedByOdin; } // Does the member have a SerializeField attribute? if (member.IsDefined <SerializeField>()) { flags |= SerializationFlags.SerializeFieldAttribute; } // Does the member have a OdinSerialize attribute? if (member.IsDefined <OdinSerializeAttribute>()) { flags |= SerializationFlags.OdinSerializeAttribute; } // Does the member have a NonSerialized attribute? if (member.IsDefined <NonSerializedAttribute>()) { flags |= SerializationFlags.NonSerializedAttribute; } // Does Unity support serializing the type? if (serializationBackend.HasAll(SerializationBackendFlags.Unity) && UnitySerializationUtility.GuessIfUnityWillSerialize(member.GetReturnType())) { flags |= SerializationFlags.TypeSupportedByUnity; } return(new MemberSerializationInfo(member, CreateNotes(member, flags, serializationBackend, serializationPolicy.ID), flags, serializationBackend)); }