Skip to content

Instantly share code, notes, and snippets.

@oxysoft
Created September 13, 2020 18:52
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save oxysoft/b790dba3125b3ae36535f52e4469e65d to your computer and use it in GitHub Desktop.
Save oxysoft/b790dba3125b3ae36535f52e4469e65d to your computer and use it in GitHub Desktop.
#if UNITY_EDITOR
using System;
using System.Linq;
using OdinExtensions;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Serialization;
using UnityEngine;
using Object = UnityEngine.Object;
[assembly: RegisterValidator(typeof(AllRequiredValidator), Priority = 500)]
namespace OdinExtensions
{
/// <summary>
/// Makes all fields that take an object to be REQUIRED by default.
/// An 'OptionalAttribute' can be used to make objects optional.
///
/// Built-in unity types and other plugins things should not be included,
/// as they can often introduce random errors since they were not designed
/// with this in mind.
///
/// Use the whitelist to select namespaces to include.
/// You can also use the blacklist to block subsections
/// of the whitelist.
/// </summary>
public class AllRequiredValidator : Validator
{
public override RevalidationCriteria RevalidationCriteria => RevalidationCriteria.OnValueChange;
// CUSTOMIZABLE WHITELIST (namespaces)
// ----------------------------------------
private static readonly string[] Whitelist =
{
// Fill me up
"Demo"
};
// CUSTOMIZABLE BLACKLIST (namespaces)
// ----------------------------------------
private static readonly string[] Blacklist =
{
// Fill me up
};
private static bool IsTypeSupported(Type type)
{
string typeNamespace = type.Namespace;
if (typeNamespace == null)
return false;
return Whitelist.Any(w => typeNamespace.StartsWith(w)) &&
!Blacklist.Any(b => typeNamespace.StartsWith(b));
}
private static bool IsValid(IPropertyValueEntry valueEntry)
{
object v = valueEntry?.WeakSmartValue;
if (v == null) return false;
if (v is Object o && o == null) return false;
if (v is string s && string.IsNullOrEmpty(s)) return false;
return true;
}
public override void RunValidation(ref ValidationResult result)
{
if (Property.ValueEntry == null) return;
if (Property.Name.StartsWith("$")) return;
if (result == null)
{
result = new ValidationResult
{
Setup = new ValidationSetup
{
Root = Property.SerializationRoot.ValueEntry.WeakValues[0] as Object,
Member = Property.Info.GetMemberInfo(),
Validator = this
},
ResultType = ValidationResultType.Valid
};
}
// Check to see that this property is allowed for AllRequired
Type parentType = Property.ParentType;
if (!IsTypeSupported(parentType))
return;
// Apply the result
if (IsValid(Property.ValueEntry))
{
result.ResultType = ValidationResultType.Valid;
result.Message = null;
}
else
{
if (Property.GetAttribute<OptionalAttribute>() != null)
return;
result.ResultType = ValidationResultType.Error;
result.Message = $"'{Property.Name}' must be assigned. All public values are serialized in Unity. Values which are not meant for configuration must be marked with NonSerialized.";
}
}
public override bool CanValidateProperty(InspectorProperty property)
{
bool is_public_field = property.Info.IsEditable && property.Info.HasBackingMembers;
bool serialized_attr = property.GetAttribute<SerializeField>() != null;
bool serialized_attr_odin = property.GetAttribute<OdinSerializeAttribute>() != null;
return is_public_field || serialized_attr || serialized_attr_odin;
}
}
}
#endif
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class OptionalAttribute : Attribute
{ }
@funnymanwin
Copy link

funnymanwin commented Oct 13, 2023

I updated your code. Compatible with new versions of Odin. Anyway u will not read this, because you abandoned this script long time ago. So I hope somebody will say thanks to me (i spent a lot of hours trying to fix it):

#if UNITY_EDITOR
using System;
using System.Linq;
using OdinExtensions;
using Sirenix.OdinInspector.Editor;
using Sirenix.OdinInspector.Editor.Validation;
using Sirenix.Serialization;
using UnityEngine;
using UnityEngine.AddressableAssets;
using Object = UnityEngine.Object;

[assembly: RegisterValidator(typeof(AllRequiredValidator), Priority = 500)]

namespace OdinExtensions
{
	/// <summary>
	/// Makes all fields that take an object to be REQUIRED by default.
	/// An 'OptionalAttribute' can be used to make objects optional.
	///
	/// Built-in unity types and other plugins things should not be included,
	/// as they can often introduce random errors since they were not designed
	/// with this in mind.
	///
	/// Use the whitelist to select namespaces to include.
	/// You can also use the blacklist to block subsections
	/// of the whitelist.
	/// </summary>
	public class AllRequiredValidator : Validator
	{
		public override RevalidationCriteria RevalidationCriteria => RevalidationCriteria.OnValueChange;
		

		private static bool IsValid(IPropertyValueEntry valueEntry)
		{
			object v = valueEntry?.WeakSmartValue;
			
			if (v == null) return false;
			if (v is Object o && o == null) return false;
			if (v is string s && string.IsNullOrEmpty(s)) return false;
			if (v is AssetReference assetReference && !assetReference.RuntimeKeyIsValid()) return false;

			return true;
		}

		public override void RunValidation(ref ValidationResult result)
		{
			if (Property.ValueEntry == null) return;
			if (Property.Name.StartsWith("$")) return;

			if (result == null)
			{
				result = new ValidationResult
				{
					Setup = new ValidationSetup
					{
						Root      = Property.SerializationRoot.ValueEntry.WeakValues[0] as Object,
						ParentInstance = Property.Parent,
						//Member    = Property.Info.GetMemberInfo(),
						Validator = this
					},
					ResultType = ValidationResultType.Valid
				};
			}


			// Apply the result
			if (IsValid(Property.ValueEntry))
			{
				result.ResultType = ValidationResultType.Valid;
				result.Message    = null;
			}
			else
			{
				if (Property.GetAttribute<OptionalAttribute>() != null)
					return;

				result.ResultType = ValidationResultType.Error;
				result.Message    = $"'{Property.Name}' must be assigned. All public values are serialized in Unity. Values which are not meant for configuration must be marked with NonSerialized.";
			}
		}

		public override bool CanValidateProperty(InspectorProperty property)
		{
			bool is_public_field      = property.Info.IsEditable && property.Info.HasBackingMembers;
			bool serialized_attr      = property.GetAttribute<SerializeField>() != null;
			bool serialized_attr_odin = property.GetAttribute<OdinSerializeAttribute>() != null;

			return is_public_field || serialized_attr || serialized_attr_odin;
		}
	}
}
#endif

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class OptionalAttribute : Attribute
{ }

image
image

Or you can just download final version as Unity Package with MiniValidationDrawer.cs from my Google Drive:
https://drive.google.com/file/d/1i1Kmzrd8fikuAZqnFYe4-jsAnwO_YnHO/view?usp=sharing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment