Added initial version 0.1.0
This commit is contained in:
parent
6aa4cb8596
commit
416759c213
64 changed files with 2181 additions and 0 deletions
280
Editor/Utilities/ProjectValidatorUtility.cs
Normal file
280
Editor/Utilities/ProjectValidatorUtility.cs
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue