using System; using System.Collections.Generic; using System.Reflection; using UnityEditor; using UnityEditor.SceneManagement; using UnityEngine; using UnityEngine.Pool; using UnityEngine.SceneManagement; namespace Module.ProjectValidator.Editor { internal static class ValidatorRunner { private static bool _initialized; private static ValidatorList _validatorList; private static TypeTree _typeTree; public static bool Run(bool showWindow = true) { if (!ProjectValidatorUtility.IsValidForRun()) return false; Initialize(); var report = new Report(); ValidateAllScenes(report); ValidateAllAssets(report); report.RebuildAssetMapping(); report.RebuildInstanceMapping(); report.SetAsActive(); ProjectValidatorUtility.RefreshUnityWindows(); if (showWindow) ProjectValidatorUtility.OpenWindow(); return true; } public static void Clear() { if (!Report.HasActive) return; Report.ClearActive(); ProjectValidatorUtility.ClearWindow(); } private static void Initialize() { if (_initialized) return; var settings = ProjectValidatorSettings.GetOrCreate(); var assemblies = GetAssembliesFrom(settings); _validatorList = new ValidatorList(); _typeTree = new TypeTree(); FetchAllValidators(); FetchAllTypesWithValidators(assemblies); FetchAllTypesWithValidators(assemblies); _initialized = true; } private static Assembly[] GetAssembliesFrom(ProjectValidatorSettings settings) { var assemblies = new List(settings.assemblies.Count); for (var i = 0; i < settings.assemblies.Count; i++) { try { var assembly = Assembly.Load(settings.assemblies[i]); assemblies.Add(assembly); } catch (Exception e) { Debug.LogException(e); } } return assemblies.ToArray(); } private static void FetchAllValidators() { var types = TypeCache.GetTypesDerivedFrom(typeof(IAttributeValidator<>)); for (var i = 0; i < types.Count; i++) { _validatorList.Add(types[i]); } } private static void FetchAllTypesWithValidators(Assembly[] assemblies) { var types = TypeCache.GetTypesDerivedFrom(); for (var i = 0; i < types.Count; i++) { var type = types[i]; if (Array.IndexOf(assemblies, type.Assembly) != -1) _typeTree.Add(type, _validatorList); } } private static void ValidateAllScenes(Report report) { var assets = EditorAssetUtility.LoadAllAssets(); var rootObjects = new List(); for (var i = 0; i < assets.Length; i++) { try { var assetPath = AssetDatabase.GetAssetPath(assets[i]); var scene = SceneManager.GetSceneByPath(assetPath); var isLoaded = scene.isLoaded; if (!isLoaded) scene = EditorSceneManager.OpenScene(assetPath, OpenSceneMode.Additive); scene.GetRootGameObjects(rootObjects); for (var j = 0; j < rootObjects.Count; j++) { ValidateGameObject(rootObjects[j], string.Empty, report); } if (!isLoaded) EditorSceneManager.CloseScene(scene, true); } catch (Exception e) { Debug.LogException(e); } } } private static void ValidateAllAssets(Report report) { ValidateAssetsBytype(report); } private static void ValidateAssetsBytype(Report report) where T : UnityEngine.Object { var assets = EditorAssetUtility.LoadAllAssets(); for (var i = 0; i < assets.Length; i++) { try { ValidateUnityObject(assets[i], report); } catch (Exception e) { Debug.LogException(e); } } } private static void ValidateUnityObject(UnityEngine.Object obj, Report report) { var assetGuid = EditorAssetUtility.ObjectToAssetGuid(obj); Validate(assetGuid, string.Empty, obj, report); } private static void ValidateGameObject(GameObject gameObject, string scenePath, Report report) { ProjectValidatorUtility.AppendToScenePath(gameObject, ref scenePath); ValidateComponents(gameObject, scenePath, report); ValidateChildren(gameObject, scenePath, report); } private static void ValidateComponents(GameObject gameObject, string scenePath, Report report) { using var _ = ListPool.Get(out var components); var assetGuid = EditorAssetUtility.GetAssetGuid(gameObject); gameObject.GetComponents(components); for (var i = 0; i < components.Count; i++) { Validate(assetGuid, scenePath, components[i], report); } } private static void ValidateChildren(GameObject gameObject, string scenePath, Report report) { var transform = gameObject.transform; for (var i = 0; i < transform.childCount; i++) { ValidateGameObject(transform.GetChild(i).gameObject, scenePath, report); } } private static void Validate(GUID assetGuid, string scenePath, object obj, Report report) { var type = obj.GetType(); if (!_typeTree.Types.TryGetValue(type, out var entry)) return; var fieldPath = obj.GetType().Name; Validate(assetGuid, scenePath, fieldPath, obj, entry, report); } private static void Validate(GUID assetGuid, string scenePath, string parentFieldPath, object obj, TypeTree.Entry entry, Report report) { if (obj == null) return; if (entry.Fields != null) { for (var i = 0; i < entry.Fields.Count; i++) { try { var field = entry.Fields[i]; var value = field.FieldInfo.GetValue(obj); var fieldPath = parentFieldPath; ProjectValidatorUtility.AppendToFieldPath(field.FieldInfo, ref fieldPath); if (value is IEnumerable ie) { var idx = 0; foreach (var eObj in ie) { var fieldPathArrElement = fieldPath; ProjectValidatorUtility.AppendToFieldPath(idx, ref fieldPathArrElement); ValidateField(field, eObj, assetGuid, scenePath, fieldPathArrElement, report); idx++; } } else { ValidateField(field, value, assetGuid, scenePath, fieldPath, report); } } catch (Exception e) { Debug.LogException(e); } } } if (entry.Entries != null) { for (var i = 0; i < entry.Entries.Count; i++) { try { var e = entry.Entries[i]; var value = e.FieldInfo.GetValue(obj); var fieldPath = parentFieldPath; ProjectValidatorUtility.AppendToFieldPath(e.FieldInfo, ref fieldPath); if (value is IEnumerable ie) { var idx = 0; foreach (var eObj in ie) { var fieldPathArrElement = fieldPath; ProjectValidatorUtility.AppendToFieldPath(idx, ref fieldPathArrElement); Validate(assetGuid, scenePath, fieldPathArrElement, eObj, e.Entry, report); idx++; } } else { Validate(assetGuid, scenePath, fieldPath, value, e.Entry, report); } } catch (Exception e) { Debug.LogException(e); } } } } private static void ValidateField(TypeTree.ValidatorField field, object value, GUID assetGuid, string scenePath, string fieldPath, Report report) { var result = (ValidatorResult)field.ValidatorMethod.Invoke(field.Validator, new[] { field.Attribute, value }); if (result.Severity != EValidatorSeverity.Valid) report.Add(assetGuid, scenePath, fieldPath, field.Attribute, result.Severity, result.Message); } } }