CORE dashboard + a lot of changes
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RPGCore.Editor.CoreManager.Validators;
|
||||
using RPGCoreCommon.Helpers;
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace RPGCore.Editor.CoreManager
|
||||
{
|
||||
internal static class ValidationSolver
|
||||
{
|
||||
private static readonly List<IValidator> Validators =
|
||||
TypeCache.GetTypesDerivedFrom<IValidator>()
|
||||
.Select(Activator.CreateInstance)
|
||||
.Cast<IValidator>()
|
||||
.ToList();
|
||||
|
||||
[InitializeOnLoadMethod]
|
||||
private static void AutomaticValidation_Initialize() => ObjectChangeEvents.changesPublished += AutomaticValidation;
|
||||
|
||||
private static void AutomaticValidation(ref ObjectChangeEventStream stream)
|
||||
{
|
||||
var instanceIds = new List<int>();
|
||||
|
||||
for (var i = 0; i < stream.length; i++)
|
||||
{
|
||||
if (stream.GetEventType(i) != ObjectChangeKind.ChangeGameObjectStructure) continue;
|
||||
|
||||
stream.GetChangeGameObjectStructureEvent(0, out var ev);
|
||||
instanceIds.Add(ev.instanceId);
|
||||
}
|
||||
|
||||
if (instanceIds.Count == 0) return;
|
||||
|
||||
var validations = Check(instanceIds);
|
||||
FixAll(validations);
|
||||
|
||||
// Can't finish because of exception or error
|
||||
validations
|
||||
.Where(v => v.result == false)
|
||||
.Select(v => "--- ERROR: " + v.resultMessage +
|
||||
"\n> " + v.contextPath +
|
||||
"\n> " + v.hierarchyPath +
|
||||
"\n> " + v.validator.GetType().Name +
|
||||
"\n> " + v.message)
|
||||
.ForEach(Debug.LogError);
|
||||
|
||||
// No fix defined
|
||||
validations
|
||||
.Where(v => v.result == null)
|
||||
.Select(v => "--- WARNING: " + v.resultMessage +
|
||||
"\n> " + v.contextPath +
|
||||
"\n> " + v.hierarchyPath +
|
||||
"\n> " + v.validator.GetType().Name +
|
||||
"\n> " + v.message)
|
||||
.ForEach(Debug.LogWarning);
|
||||
}
|
||||
|
||||
internal static List<Validation> Check(List<int> instanceIds)
|
||||
{
|
||||
var validations = new List<Validation>();
|
||||
|
||||
foreach (var instanceId in instanceIds)
|
||||
{
|
||||
var obj = EditorUtility.EntityIdToObject(instanceId);
|
||||
if (obj is not GameObject go) continue;
|
||||
|
||||
Check(go, validations, false);
|
||||
}
|
||||
|
||||
return validations;
|
||||
}
|
||||
|
||||
internal static List<Validation> Check(string[] paths)
|
||||
{
|
||||
var validations = new List<Validation>();
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssetGUIDs("t:Scene", paths))
|
||||
{
|
||||
var scene = EditorSceneManager.OpenPreviewScene(AssetDatabase.GUIDToAssetPath(guid));
|
||||
foreach (var go in scene.GetRootGameObjects())
|
||||
{
|
||||
try
|
||||
{
|
||||
Check(go, validations, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
EditorSceneManager.ClosePreviewScene(scene);
|
||||
}
|
||||
|
||||
foreach (var guid in AssetDatabase.FindAssetGUIDs("t:Prefab", paths))
|
||||
{
|
||||
var prefabPath = AssetDatabase.GUIDToAssetPath(guid);
|
||||
var prefab = PrefabUtility.LoadPrefabContents(prefabPath);
|
||||
|
||||
try
|
||||
{
|
||||
Check(prefab, validations, true);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
PrefabUtility.UnloadPrefabContents(prefab);
|
||||
}
|
||||
|
||||
return validations;
|
||||
}
|
||||
|
||||
internal static void Check(GameObject go, List<Validation> result, bool recursive)
|
||||
{
|
||||
// On scene and part of other prefab - don't validate that, it will be validated separately anyway
|
||||
if (PrefabUtility.IsPartOfPrefabInstance(go)) return;
|
||||
|
||||
foreach (var validator in Validators)
|
||||
{
|
||||
var message = validator.Check(go);
|
||||
if (string.IsNullOrEmpty(message)) continue;
|
||||
|
||||
result.Add(new Validation
|
||||
{
|
||||
validator = validator,
|
||||
message = message,
|
||||
contextPath = GetContextPath(go),
|
||||
hierarchyPath = GetHierarchyPath(go),
|
||||
});
|
||||
}
|
||||
|
||||
if (!recursive) return;
|
||||
for (var i = 0; i < go.transform.childCount; i++)
|
||||
Check(go.transform.GetChild(i).gameObject, result, true);
|
||||
}
|
||||
|
||||
internal static void FixAll(List<Validation> validations)
|
||||
{
|
||||
foreach (var validation in validations)
|
||||
{
|
||||
var contextType = AssetDatabase.GetMainAssetTypeAtPath(validation.contextPath);
|
||||
|
||||
if (contextType == typeof(SceneAsset)) FixOnScene(validation);
|
||||
else if (contextType == typeof(GameObject)) FixOnPrefab(validation);
|
||||
else throw new Exception($"Given file '{validation.contextPath}' is not prefab nor scene!");
|
||||
}
|
||||
}
|
||||
|
||||
private static void FixOnScene(Validation validation)
|
||||
{
|
||||
var scene = Enumerable.Range(0, SceneManager.loadedSceneCount)
|
||||
.Select(SceneManager.GetSceneAt)
|
||||
.FirstOrDefault(scene => scene.path == validation.contextPath);
|
||||
var isActive = scene.IsValid();
|
||||
if (!isActive) scene = EditorSceneManager.OpenPreviewScene(validation.contextPath);
|
||||
var go = FindGameObject(scene.GetRootGameObjects(), validation);
|
||||
FixGameObject(go, validation);
|
||||
if (isActive) EditorUtility.SetDirty(go);
|
||||
if (!isActive) EditorSceneManager.SaveScene(scene);
|
||||
if (!isActive) EditorSceneManager.ClosePreviewScene(scene);
|
||||
}
|
||||
|
||||
private static void FixOnPrefab(Validation validation)
|
||||
{
|
||||
var prefabStage = PrefabStageUtility.GetCurrentPrefabStage();
|
||||
var isActive = prefabStage && prefabStage.assetPath == validation.contextPath;
|
||||
var prefabRoot = isActive
|
||||
? prefabStage.prefabContentsRoot
|
||||
: PrefabUtility.LoadPrefabContents(validation.contextPath);
|
||||
var go = FindGameObject(new[] { prefabRoot }, validation);
|
||||
FixGameObject(go, validation);
|
||||
if (isActive) EditorUtility.SetDirty(go);
|
||||
if (!isActive) PrefabUtility.SaveAsPrefabAsset(prefabRoot, validation.contextPath);
|
||||
if (!isActive) PrefabUtility.UnloadPrefabContents(prefabRoot);
|
||||
}
|
||||
|
||||
private static GameObject FindGameObject(GameObject[] roots, Validation validation)
|
||||
{
|
||||
var hierarchyPathArray = validation.hierarchyPath.Split('/', 2);
|
||||
var root = roots.FirstOrDefault(go => go.name == hierarchyPathArray[0]);
|
||||
if (!root) throw new Exception($"Root '{hierarchyPathArray[0]}' not found in '{validation.contextPath}'!");
|
||||
var go = hierarchyPathArray.Length > 1 ? root.transform.Find(hierarchyPathArray[1])?.gameObject : root;
|
||||
if (!go) throw new Exception($"GameObject '{validation.hierarchyPath}' not found in '{validation.contextPath}'!");
|
||||
return go;
|
||||
}
|
||||
|
||||
private static void FixGameObject(GameObject go, Validation validation)
|
||||
{
|
||||
try
|
||||
{
|
||||
validation.validator.Fix(go);
|
||||
validation.resultMessage = "Done.";
|
||||
validation.result = true;
|
||||
}
|
||||
catch (NotImplementedException)
|
||||
{
|
||||
validation.resultMessage = "No Auto-fix defined. Do it yourself.";
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
validation.result = false;
|
||||
validation.resultMessage = e.Message;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetContextPath(GameObject go)
|
||||
{
|
||||
// ON PREFAB STAGE - this one will be used only in realtime correcting, never in verify tab
|
||||
if (PrefabStageUtility.GetPrefabStage(go) is {} stage) return stage.assetPath;
|
||||
|
||||
// ON SCENE
|
||||
if (go.scene.IsValid()) return go.scene.path;
|
||||
|
||||
// PREFAB ASSET
|
||||
if (PrefabUtility.IsPartOfPrefabAsset(go)) return go.scene.path;
|
||||
|
||||
throw new Exception("No valid context path found, sad pepe :(");
|
||||
}
|
||||
|
||||
private static string GetHierarchyPath(GameObject go)
|
||||
{
|
||||
var path = new List<string>();
|
||||
|
||||
while (go is not null)
|
||||
{
|
||||
path.Add(go.name);
|
||||
go = go.transform.parent?.gameObject;
|
||||
}
|
||||
|
||||
return string.Join("/", path.AsEnumerable().Reverse());
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user