module-project-validator/Editor/Utilities/ProjectValidatorUtility.cs

280 lines
No EOL
10 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.SceneManagement;
namespace Module.ProjectValidator.Editor
{
public static class ProjectValidatorUtility
{
[MenuItem("Window/Analysis/Project Validator")]
public static void OpenWindow()
{
var window = InternalOpenWindow();
window.Show();
window.Rebuild();
}
public static void ClearWindow()
{
var windows = Resources.FindObjectsOfTypeAll<EditorProjectValidatorWindow>();
for (var i = 0; i < windows.Length; i++)
{
windows[i].Clear();
}
RefreshUnityWindows();
}
private static EditorProjectValidatorWindow InternalOpenWindow()
{
var window = EditorWindow.GetWindow<EditorProjectValidatorWindow>();
window.titleContent = new GUIContent("Project Validator");
return window;
}
internal static string GetAttributeShortName(Attribute attribute)
{
var str = attribute.GetType().Name;
var index = str.IndexOf("Attribute", StringComparison.Ordinal);
if (index != -1)
str = str[..index];
str = ObjectNames.NicifyVariableName(str);
return str;
}
internal static void AppendToScenePath(GameObject gameObject, ref string scenePath)
{
scenePath = string.IsNullOrEmpty(scenePath) ? gameObject.name : $"{scenePath}/{gameObject.name}";
}
internal static string ApplyRichTextToScenePath(string scenePath)
{
return scenePath.Replace("/", "<color=#00ff00><b>/</b></color>");
}
public static void AppendToFieldPath(FieldInfo fieldInfo, ref string fieldPath)
{
fieldPath = string.IsNullOrEmpty(fieldPath) ? fieldInfo.Name : $"{fieldPath}.{fieldInfo.Name}";
}
public static void AppendToFieldPath(int index, ref string fieldPath)
{
fieldPath += $"[{index}]";
}
public static string ApplyRichTextToFieldPath(string fieldPath)
{
var str = fieldPath.Replace(".", "<color=#00ff00><b>.</b></color>");
str = str.Replace("[", "<color=#00ff00><b>[</b></color>");
str = str.Replace("]", "<color=#00ff00><b>]</b></color>");
return str;
}
internal static void PingObject(Report.Entry entry)
{
if (entry.AssetGuid.Empty())
return;
var assetPath = AssetDatabase.GUIDToAssetPath(entry.AssetGuid);
var asset = AssetDatabase.LoadMainAssetAtPath(assetPath);
if (asset == null)
return;
if (asset is SceneAsset)
{
var scene = SceneManager.GetSceneByPath(assetPath);
if (scene.isLoaded && TryFindSceneObjectByPath(scene, entry.ScenePath, out var gameObject))
EditorGUIUtility.PingObject(gameObject);
else
EditorGUIUtility.PingObject(asset);
}
else
{
EditorGUIUtility.PingObject(asset);
}
}
private static bool TryFindSceneObjectByPath(Scene scene, string scenePath, out GameObject gameObject)
{
using var _ = ListPool<GameObject>.Get(out var rootObjects);
scene.GetRootGameObjects(rootObjects);
var index = scenePath.IndexOf('/');
var rootName = index != -1 ? scenePath[..index] : scenePath;
var childPath = index != -1 ? scenePath[(index + 1)..] : string.Empty;
for (var i = 0; i < rootObjects.Count; i++)
{
var rootObject = rootObjects[i];
if (rootObject.name != rootName)
continue;
if (string.IsNullOrEmpty(childPath))
{
gameObject = rootObject;
return true;
}
var child = rootObject.transform.Find(childPath);
if (child == null)
continue;
gameObject = child.gameObject;
return true;
}
gameObject = null;
return false;
}
internal static void RebuildAssetMapping(Dictionary<GUID, Report.MappingEntry> dictMapping)
{
using var _ = DictionaryPool<GUID, Report.MappingEntry>.Get(out var newMappings);
foreach (var pair in dictMapping)
{
var severity = pair.Value.Severity;
var assetPath = AssetDatabase.GUIDToAssetPath(pair.Key);
var folderPath = Path.GetDirectoryName(assetPath);
while (!string.IsNullOrEmpty(folderPath))
{
var strGuid = AssetDatabase.AssetPathToGUID(folderPath);
if (GUID.TryParse(strGuid, out var assetGuid))
{
if (dictMapping.TryGetValue(assetGuid, out var parentMapping))
{
if (severity < parentMapping.Severity)
severity = parentMapping.Severity;
}
else if (newMappings.TryGetValue(assetGuid, out var currentMapping))
{
if (currentMapping.Severity < severity)
newMappings[assetGuid] = new Report.MappingEntry(severity, true);
}
else
{
newMappings.Add(assetGuid, new Report.MappingEntry(severity, true));
}
}
folderPath = Path.GetDirectoryName(folderPath);
}
}
foreach (var pair in newMappings)
{
dictMapping.Add(pair.Key, pair.Value);
}
}
internal static void RebuildSceneInstanceMapping(Report report, Dictionary<int, Report.MappingEntry> dictMapping)
{
dictMapping.Clear();
using var _ = ListPool<GameObject>.Get(out var rootObjects);
for (var i = 0; i < SceneManager.sceneCount; i++)
{
var scene = SceneManager.GetSceneAt(i);
if (!scene.isLoaded)
continue;
var strAssetGuid = AssetDatabase.AssetPathToGUID(scene.path);
GUID.TryParse(strAssetGuid, out var assetGuid);
scene.GetRootGameObjects(rootObjects);
for (var j = 0; j < rootObjects.Count; j++)
{
var rootObject = rootObjects[j];
var scenePath = string.Empty;
RebuildSceneInstanceMapping(report, dictMapping, rootObject, assetGuid, scenePath);
}
}
RebuildForAllParents(dictMapping);
}
private static void RebuildSceneInstanceMapping(Report report, Dictionary<int, Report.MappingEntry> dictMapping, GameObject gameObject, GUID assetGuid, string scenePath)
{
var transform = gameObject.transform;
AppendToScenePath(gameObject, ref scenePath);
if (report.TryGetSeverityFor(assetGuid, scenePath, out var mapping))
dictMapping.Add(gameObject.GetInstanceID(), new Report.MappingEntry(mapping.Severity, false));
for (var i = 0; i < transform.childCount; i++)
{
RebuildSceneInstanceMapping(report, dictMapping, transform.GetChild(i).gameObject, assetGuid, scenePath);
}
}
private static void RebuildForAllParents(Dictionary<int, Report.MappingEntry> dictMapping)
{
using var _ = DictionaryPool<int, Report.MappingEntry>.Get(out var newMappings);
foreach (var pair in dictMapping)
{
var obj = EditorUtility.EntityIdToObject(pair.Key);
if (obj is not GameObject gameObject)
continue;
var severity = pair.Value.Severity;
var transform = gameObject.transform.parent;
while (transform != null)
{
gameObject = transform.gameObject;
var instanceId = gameObject.GetInstanceID();
if (dictMapping.TryGetValue(instanceId, out var parentMapping))
{
if (severity < parentMapping.Severity)
severity = parentMapping.Severity;
}
else if (newMappings.TryGetValue(instanceId, out var currentMapping))
{
if (currentMapping.Severity < severity)
newMappings[instanceId] = new Report.MappingEntry(severity, true);
}
else
{
newMappings.Add(instanceId, new Report.MappingEntry(severity, true));
}
transform = transform.parent;
}
}
foreach (var pair in newMappings)
{
dictMapping.Add(pair.Key, pair.Value);
}
}
internal static void RefreshUnityWindows()
{
EditorApplication.RepaintHierarchyWindow();
EditorApplication.RepaintProjectWindow();
}
internal static bool IsValidForRun()
{
return !EditorApplication.isPlaying && !EditorApplication.isPlayingOrWillChangePlaymode && !EditorApplication.isCompiling;
}
}
}