395 lines
No EOL
14 KiB
C#
395 lines
No EOL
14 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Reflection;
|
|
using UnityEditor;
|
|
using UnityEditor.SceneManagement;
|
|
using UnityEngine;
|
|
using UnityEngine.Pool;
|
|
using UnityEngine.SceneManagement;
|
|
using Debug = UnityEngine.Debug;
|
|
|
|
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;
|
|
|
|
var stopwatch = new Stopwatch();
|
|
stopwatch.Start();
|
|
|
|
Initialize();
|
|
|
|
var report = new Report();
|
|
ValidateAllScenes(report);
|
|
ValidateAllAssets(report);
|
|
ValidateAllPrefabs(report);
|
|
report.RebuildAssetMapping();
|
|
report.RebuildInstanceMapping();
|
|
report.SetAsActive();
|
|
|
|
ProjectValidatorUtility.RefreshUnityWindows();
|
|
|
|
if (showWindow)
|
|
ProjectValidatorUtility.OpenWindow();
|
|
|
|
stopwatch.Stop();
|
|
Debug.Log($"Validator took {stopwatch.Elapsed.TotalMilliseconds}ms");
|
|
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();
|
|
|
|
FetchAllGameObjectValidators();
|
|
FetchAllComponentValidators();
|
|
FetchAllAttributeValidators();
|
|
FetchAllTypesWithValidators<Component>(assemblies);
|
|
FetchAllTypesWithValidators<ScriptableObject>(assemblies);
|
|
_initialized = true;
|
|
}
|
|
|
|
private static Assembly[] GetAssembliesFrom(ProjectValidatorSettings settings)
|
|
{
|
|
var assemblies = new List<Assembly>(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 FetchAllAttributeValidators()
|
|
{
|
|
var types = TypeCache.GetTypesDerivedFrom(typeof(IAttributeValidator<>));
|
|
|
|
for (var i = 0; i < types.Count; i++)
|
|
{
|
|
_validatorList.AddAttribute(types[i]);
|
|
}
|
|
}
|
|
|
|
private static void FetchAllGameObjectValidators()
|
|
{
|
|
var types = TypeCache.GetTypesDerivedFrom(typeof(IGameObjectValidator));
|
|
|
|
for (var i = 0; i < types.Count; i++)
|
|
{
|
|
_validatorList.AddGameObject(types[i]);
|
|
}
|
|
}
|
|
|
|
private static void FetchAllComponentValidators()
|
|
{
|
|
var types = TypeCache.GetTypesDerivedFrom(typeof(IComponentValidator<>));
|
|
|
|
for (var i = 0; i < types.Count; i++)
|
|
{
|
|
_validatorList.AddComponent(types[i]);
|
|
}
|
|
}
|
|
|
|
private static void FetchAllTypesWithValidators<T>(Assembly[] assemblies)
|
|
{
|
|
var types = TypeCache.GetTypesDerivedFrom<T>();
|
|
|
|
for (var i = 0; i < types.Count; i++)
|
|
{
|
|
var type = types[i];
|
|
|
|
if (assemblies.Length == 0 || Array.IndexOf(assemblies, type.Assembly) != -1)
|
|
_typeTree.Add(type, _validatorList);
|
|
}
|
|
}
|
|
|
|
private static void ValidateAllScenes(Report report)
|
|
{
|
|
var assets = EditorAssetUtility.LoadAllAssets<SceneAsset>();
|
|
var rootObjects = new List<GameObject>();
|
|
|
|
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], "scene:", report, true);
|
|
}
|
|
|
|
if (!isLoaded)
|
|
EditorSceneManager.CloseScene(scene, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateAllAssets(Report report)
|
|
{
|
|
ValidateAssetsBytype<ScriptableObject>(report);
|
|
}
|
|
|
|
private static void ValidateAllPrefabs(Report report)
|
|
{
|
|
var assets = EditorAssetUtility.LoadAllAssets<GameObject>();
|
|
|
|
for (var i = 0; i < assets.Length; i++)
|
|
{
|
|
try
|
|
{
|
|
ValidateGameObject(assets[i], "prefab:", report, true);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateAssetsBytype<T>(Report report) where T : UnityEngine.Object
|
|
{
|
|
var assets = EditorAssetUtility.LoadAllAssets<T>();
|
|
|
|
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);
|
|
var assetPath = AssetDatabase.GUIDToAssetPath(assetGuid);
|
|
Validate(assetGuid, $"asset:{assetPath}", obj, report);
|
|
}
|
|
|
|
private static void ValidateGameObject(GameObject gameObject, string relativePath, Report report, bool initial)
|
|
{
|
|
ProjectValidatorUtility.AppendToRelativePath(gameObject, ref relativePath, initial);
|
|
|
|
var assetGuid = EditorAssetUtility.GetAssetGuid(gameObject);
|
|
using var _ = ListPool<ValidatorResult>.Get(out var results);
|
|
|
|
for (var i = 0; i < _validatorList.GameObjectValidators.Count; i++)
|
|
{
|
|
results.Clear();
|
|
|
|
_validatorList.GameObjectValidators[i].Validate(gameObject, results);
|
|
var type = ProjectValidatorUtility.GetGameObjectValidatorName(_validatorList.GameObjectValidators[i]);
|
|
|
|
for (var j = 0; j < results.Count; j++)
|
|
{
|
|
var result = results[j];
|
|
|
|
if (result.Severity != EValidatorSeverity.Valid)
|
|
report.Add(assetGuid, relativePath, string.Empty, type, result.Severity, result.Message);
|
|
}
|
|
}
|
|
|
|
ValidateComponents(gameObject, assetGuid, relativePath, report);
|
|
ValidateChildren(gameObject, relativePath, report);
|
|
}
|
|
|
|
private static void ValidateComponents(GameObject gameObject, GUID assetGuid, string relativePath, Report report)
|
|
{
|
|
using var _ = ListPool<Component>.Get(out var components);
|
|
gameObject.GetComponents(components);
|
|
|
|
for (var i = 0; i < components.Count; i++)
|
|
{
|
|
if (components[i] != null)
|
|
Validate(assetGuid, relativePath, components[i], report);
|
|
}
|
|
}
|
|
|
|
private static void ValidateChildren(GameObject gameObject, string relativePath, Report report)
|
|
{
|
|
var transform = gameObject.transform;
|
|
|
|
for (var i = 0; i < transform.childCount; i++)
|
|
{
|
|
ValidateGameObject(transform.GetChild(i).gameObject, relativePath, report, false);
|
|
}
|
|
}
|
|
|
|
private static void Validate(GUID assetGuid, string relativePath, object obj, Report report)
|
|
{
|
|
var type = obj.GetType();
|
|
|
|
if (!_typeTree.Types.TryGetValue(type, out var entry))
|
|
return;
|
|
|
|
var fieldPath = obj.GetType().Name;
|
|
Validate(assetGuid, relativePath, fieldPath, obj, entry, report);
|
|
}
|
|
|
|
private static void Validate(GUID assetGuid, string relativePath, string parentFieldPath, object obj, TypeTree.Entry entry, Report report)
|
|
{
|
|
if (obj == null)
|
|
return;
|
|
|
|
if (entry.Components != null)
|
|
{
|
|
for (var i = 0; i < entry.Components.Count; i++)
|
|
{
|
|
try
|
|
{
|
|
var component = entry.Components[i];
|
|
ValidateComponent(component, obj, assetGuid, relativePath, report);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
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<object> ie)
|
|
{
|
|
var idx = 0;
|
|
|
|
foreach (var eObj in ie)
|
|
{
|
|
var fieldPathArrElement = fieldPath;
|
|
ProjectValidatorUtility.AppendToFieldPath(idx, ref fieldPathArrElement);
|
|
ValidateField(field, eObj, assetGuid, relativePath, fieldPathArrElement, report);
|
|
idx++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ValidateField(field, value, assetGuid, relativePath, 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<object> ie)
|
|
{
|
|
var idx = 0;
|
|
|
|
foreach (var eObj in ie)
|
|
{
|
|
var fieldPathArrElement = fieldPath;
|
|
ProjectValidatorUtility.AppendToFieldPath(idx, ref fieldPathArrElement);
|
|
Validate(assetGuid, relativePath, fieldPathArrElement, eObj, e.Entry, report);
|
|
idx++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Validate(assetGuid, relativePath, fieldPath, value, e.Entry, report);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Debug.LogException(e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void ValidateField(TypeTree.ValidatorField field, object value, GUID assetGuid, string relativePath, string fieldPath, Report report)
|
|
{
|
|
var result = (ValidatorResult)field.ValidatorMethod.Invoke(field.Validator, new[] { field.Attribute, value });
|
|
|
|
if (result.Severity != EValidatorSeverity.Valid)
|
|
report.Add(assetGuid, relativePath, fieldPath, field.Attribute, result.Severity, result.Message);
|
|
}
|
|
|
|
private static void ValidateComponent(TypeTree.ValidatorComponent component, object value, GUID assetGuid, string relativePath, Report report)
|
|
{
|
|
using var _ = ListPool<ValidatorResult>.Get(out var results);
|
|
component.ValidatorMethod.Invoke(component.Validator, new[] { value, results });
|
|
var type = ProjectValidatorUtility.GetComponentValidatorShortName(component.Validator);
|
|
|
|
for (var i = 0; i < results.Count; i++)
|
|
{
|
|
var result = results[i];
|
|
|
|
if (result.Severity != EValidatorSeverity.Valid)
|
|
report.Add(assetGuid, relativePath, string.Empty, type, result.Severity, result.Message);
|
|
}
|
|
}
|
|
}
|
|
} |