216 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
		
		
			
		
	
	
			216 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			C#
		
	
	
	
|  | // ReSharper disable once RedundantUsingDirective | |||
|  | using System.Linq; | |||
|  | 
 | |||
|  | namespace SRF | |||
|  | { | |||
|  |     using System; | |||
|  |     using System.Collections.Generic; | |||
|  |     using System.Reflection; | |||
|  |     using Helpers; | |||
|  |     using Service; | |||
|  |     using UnityEngine; | |||
|  | 
 | |||
|  |     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field)] | |||
|  |     public sealed class RequiredFieldAttribute : Attribute | |||
|  |     { | |||
|  |         private bool _autoCreate; | |||
|  |         private bool _autoSearch; | |||
|  |         private bool _editorOnly = true; | |||
|  | 
 | |||
|  |         public RequiredFieldAttribute(bool autoSearch) | |||
|  |         { | |||
|  |             AutoSearch = autoSearch; | |||
|  |         } | |||
|  | 
 | |||
|  |         public RequiredFieldAttribute() {} | |||
|  | 
 | |||
|  |         public bool AutoSearch | |||
|  |         { | |||
|  |             get { return _autoSearch; } | |||
|  |             set { _autoSearch = value; } | |||
|  |         } | |||
|  | 
 | |||
|  |         public bool AutoCreate | |||
|  |         { | |||
|  |             get { return _autoCreate; } | |||
|  |             set { _autoCreate = value; } | |||
|  |         } | |||
|  | 
 | |||
|  |         [Obsolete] | |||
|  |         public bool EditorOnly | |||
|  |         { | |||
|  |             get { return _editorOnly; } | |||
|  |             set { _editorOnly = value; } | |||
|  |         } | |||
|  |     } | |||
|  | 
 | |||
|  |     /// <summary> | |||
|  |     /// Add to a field to attempt to use SRServiceManager to get an instance of the field type | |||
|  |     /// </summary> | |||
|  |     [AttributeUsage(AttributeTargets.Field)] | |||
|  |     public class ImportAttribute : Attribute | |||
|  |     { | |||
|  |         public readonly Type Service; | |||
|  |         public ImportAttribute() {} | |||
|  | 
 | |||
|  |         public ImportAttribute(Type serviceType) | |||
|  |         { | |||
|  |             Service = serviceType; | |||
|  |         } | |||
|  |     } | |||
|  | 
 | |||
|  |     public abstract class SRMonoBehaviourEx : SRMonoBehaviour | |||
|  |     { | |||
|  |         private static Dictionary<Type, IList<FieldInfo>> _checkedFields; | |||
|  | 
 | |||
|  |         private static void CheckFields(SRMonoBehaviourEx instance, bool justSet = false) | |||
|  |         { | |||
|  |             if (_checkedFields == null) | |||
|  |             { | |||
|  |                 _checkedFields = new Dictionary<Type, IList<FieldInfo>>(); | |||
|  |             } | |||
|  | 
 | |||
|  |             var t = instance.GetType(); | |||
|  | 
 | |||
|  |             IList<FieldInfo> cache; | |||
|  | 
 | |||
|  |             if (!_checkedFields.TryGetValue(instance.GetType(), out cache)) | |||
|  |             { | |||
|  |                 cache = ScanType(t); | |||
|  | 
 | |||
|  |                 _checkedFields.Add(t, cache); | |||
|  |             } | |||
|  | 
 | |||
|  |             PopulateObject(cache, instance, justSet); | |||
|  |         } | |||
|  | 
 | |||
|  |         private static void PopulateObject(IList<FieldInfo> cache, SRMonoBehaviourEx instance, bool justSet) | |||
|  |         { | |||
|  |             for (var i = 0; i < cache.Count; i++) | |||
|  |             { | |||
|  |                 var f = cache[i]; | |||
|  | 
 | |||
|  |                 if (!EqualityComparer<object>.Default.Equals(f.Field.GetValue(instance), null)) | |||
|  |                 { | |||
|  |                     continue; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // If import is enabled, use SRServiceManager to import the reference | |||
|  |                 if (f.Import) | |||
|  |                 { | |||
|  |                     var t = f.ImportType ?? f.Field.FieldType; | |||
|  | 
 | |||
|  |                     var service = SRServiceManager.GetService(t); | |||
|  | 
 | |||
|  |                     if (service == null) | |||
|  |                     { | |||
|  |                         Debug.LogWarning("Field {0} import failed (Type {1})".Fmt(f.Field.Name, t)); | |||
|  |                         continue; | |||
|  |                     } | |||
|  | 
 | |||
|  |                     f.Field.SetValue(instance, service); | |||
|  | 
 | |||
|  |                     continue; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 // If autoset is enabled on field, try and find the component on the GameObject | |||
|  | 
 | |||
|  |                 if (f.AutoSet) | |||
|  |                 { | |||
|  |                     var newValue = instance.GetComponent(f.Field.FieldType); | |||
|  | 
 | |||
|  |                     if (!EqualityComparer<object>.Default.Equals(newValue, null)) | |||
|  |                     { | |||
|  |                         f.Field.SetValue(instance, newValue); | |||
|  |                         continue; | |||
|  |                     } | |||
|  |                 } | |||
|  | 
 | |||
|  |                 if (justSet) | |||
|  |                 { | |||
|  |                     continue; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 if (f.AutoCreate) | |||
|  |                 { | |||
|  |                     var newValue = instance.CachedGameObject.AddComponent(f.Field.FieldType); | |||
|  |                     f.Field.SetValue(instance, newValue); | |||
|  |                 } | |||
|  | 
 | |||
|  |                 throw new UnassignedReferenceException( | |||
|  |                     "Field {0} is unassigned, but marked with RequiredFieldAttribute".Fmt(f.Field.Name)); | |||
|  |             } | |||
|  |         } | |||
|  | 
 | |||
|  |         private static List<FieldInfo> ScanType(Type t) | |||
|  |         { | |||
|  |             var cache = new List<FieldInfo>(); | |||
|  | 
 | |||
|  |             // Check for attribute added to the class | |||
|  |             var globalAttr = SRReflection.GetAttribute<RequiredFieldAttribute>(t); | |||
|  | 
 | |||
|  | #if NETFX_CORE | |||
|  | 		var fields = t.GetTypeInfo().DeclaredFields.Where(f => !f.IsStatic); | |||
|  | #else | |||
|  |             // Check each field for the attribute | |||
|  |             var fields = t.GetFields(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); | |||
|  | #endif | |||
|  | 
 | |||
|  |             foreach (var f in fields) | |||
|  |             { | |||
|  |                 var requiredFieldAttribute = SRReflection.GetAttribute<RequiredFieldAttribute>(f); | |||
|  |                 var importAttribute = SRReflection.GetAttribute<ImportAttribute>(f); | |||
|  | 
 | |||
|  |                 if (globalAttr == null && requiredFieldAttribute == null && importAttribute == null) | |||
|  |                 { | |||
|  |                     continue; // Early out if no attributes found. | |||
|  |                 } | |||
|  | 
 | |||
|  |                 var info = new FieldInfo(); | |||
|  |                 info.Field = f; | |||
|  | 
 | |||
|  |                 if (importAttribute != null) | |||
|  |                 { | |||
|  |                     info.Import = true; | |||
|  |                     info.ImportType = importAttribute.Service; | |||
|  |                 } | |||
|  |                 else if (requiredFieldAttribute != null) | |||
|  |                 { | |||
|  |                     info.AutoSet = requiredFieldAttribute.AutoSearch; | |||
|  |                     info.AutoCreate = requiredFieldAttribute.AutoCreate; | |||
|  |                 } | |||
|  |                 else | |||
|  |                 { | |||
|  |                     info.AutoSet = globalAttr.AutoSearch; | |||
|  |                     info.AutoCreate = globalAttr.AutoCreate; | |||
|  |                 } | |||
|  | 
 | |||
|  |                 cache.Add(info); | |||
|  |             } | |||
|  | 
 | |||
|  |             return cache; | |||
|  |         } | |||
|  | 
 | |||
|  |         protected virtual void Awake() | |||
|  |         { | |||
|  |             CheckFields(this); | |||
|  |         } | |||
|  | 
 | |||
|  |         protected virtual void Start() {} | |||
|  |         protected virtual void Update() {} | |||
|  |         protected virtual void FixedUpdate() {} | |||
|  |         protected virtual void OnEnable() {} | |||
|  |         protected virtual void OnDisable() {} | |||
|  |         protected virtual void OnDestroy() {} | |||
|  | 
 | |||
|  |         private struct FieldInfo | |||
|  |         { | |||
|  |             public bool AutoCreate; | |||
|  |             public bool AutoSet; | |||
|  |             public System.Reflection.FieldInfo Field; | |||
|  |             public bool Import; | |||
|  |             public Type ImportType; | |||
|  |         } | |||
|  |     } | |||
|  | } |