diff --git a/Editor/ErrorReporting/ComponentValidation.cs b/Editor/ErrorReporting/ComponentValidation.cs
index 81bb0946..e80b2224 100644
--- a/Editor/ErrorReporting/ComponentValidation.cs
+++ b/Editor/ErrorReporting/ComponentValidation.cs
@@ -1,5 +1,6 @@
using System.Collections.Generic;
using nadena.dev.modular_avatar.core;
+using nadena.dev.ndmf;
#if MA_VRCSDK3_AVATARS
using nadena.dev.modular_avatar.core.menu;
@@ -16,64 +17,61 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
///
///
/// Null if valid, otherwise a list of configuration errors
- internal static List CheckComponent(this AvatarTagComponent tagComponent)
+ internal static void CheckComponent(this AvatarTagComponent tagComponent)
{
- switch (tagComponent)
+ ErrorReport.WithContextObject(tagComponent, () =>
{
- case ModularAvatarBlendshapeSync bs:
- return CheckInternal(bs);
- case ModularAvatarBoneProxy bp:
- return CheckInternal(bp);
+ switch (tagComponent)
+ {
+ case ModularAvatarBlendshapeSync bs:
+ CheckInternal(bs);
+ break;
+ case ModularAvatarBoneProxy bp:
+ CheckInternal(bp);
+ break;
#if MA_VRCSDK3_AVATARS
- case ModularAvatarMenuInstaller mi:
- return CheckInternal(mi);
- case ModularAvatarMergeAnimator obj:
- return CheckInternal(obj);
+ case ModularAvatarMenuInstaller mi:
+ CheckInternal(mi);
+ break;
+ case ModularAvatarMergeAnimator obj:
+ CheckInternal(obj);
+ break;
#endif
- case ModularAvatarMergeArmature obj:
- return CheckInternal(obj);
- default:
- return null;
- }
+ case ModularAvatarMergeArmature obj:
+ CheckInternal(obj);
+ break;
+ default:
+ return;
+ }
+ });
}
- internal static List ValidateAll(GameObject root)
+ internal static void ValidateAll(GameObject root)
{
- List logs = new List();
foreach (var component in root.GetComponentsInChildren(true))
{
- var componentLogs = component.CheckComponent();
- if (componentLogs != null)
- {
- logs.AddRange(componentLogs);
- }
+ component.CheckComponent();
}
-
- return logs;
}
- private static List CheckInternal(ModularAvatarBlendshapeSync bs)
- {
+ private static void CheckInternal(ModularAvatarBlendshapeSync bs)
+ {
var localMesh = bs.GetComponent();
if (localMesh == null)
{
- return new List
- {new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.no_local_renderer", bs)};
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_local_renderer", bs);
}
if (localMesh.sharedMesh == null)
{
- return new List
- {new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.no_local_mesh", bs)};
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_local_mesh", bs);
}
if (bs.Bindings == null || bs.Bindings.Count == 0)
{
- return new List
- {new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.no_bindings", bs)};
+ BuildReport.Log(ErrorSeverity.Information,"validation.blendshape_sync.no_bindings", bs);
}
- List errorLogs = new List();
foreach (var binding in bs.Bindings)
{
var localShape = string.IsNullOrWhiteSpace(binding.LocalBlendshape)
@@ -82,113 +80,82 @@ namespace nadena.dev.modular_avatar.editor.ErrorReporting
if (localMesh.sharedMesh.GetBlendShapeIndex(localShape) == -1)
{
- errorLogs.Add(new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.missing_local_shape",
- new string[] {localShape}, bs));
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_local_shape",
+ localShape, bs);
}
var targetObj = binding.ReferenceMesh.Get(bs.transform);
if (targetObj == null)
{
- errorLogs.Add(new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.no_target", bs));
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.no_target", bs);
continue;
}
var targetRenderer = targetObj.GetComponent();
if (targetRenderer == null)
{
- errorLogs.Add(new ErrorLog(ReportLevel.Validation,
- "validation.blendshape_sync.missing_target_renderer", bs, targetRenderer));
+ BuildReport.Log(ErrorSeverity.NonFatal,
+ "validation.blendshape_sync.missing_target_renderer", bs, targetRenderer);
continue;
}
var targetMesh = targetRenderer.sharedMesh;
if (targetMesh == null)
{
- errorLogs.Add(new ErrorLog(ReportLevel.Validation, "validation.blendshape_sync.missing_target_mesh",
- bs, targetRenderer));
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.blendshape_sync.missing_target_mesh",
+ bs, targetRenderer);
continue;
}
if (targetMesh.GetBlendShapeIndex(binding.Blendshape) == -1)
{
- errorLogs.Add(new ErrorLog(ReportLevel.Validation,
- "validation.blendshape_sync.missing_target_shape", new string[] {binding.Blendshape}, bs,
- targetRenderer));
+ BuildReport.Log(ErrorSeverity.NonFatal,
+ "validation.blendshape_sync.missing_target_shape", binding.Blendshape, bs,
+ targetRenderer);
}
}
-
- if (errorLogs.Count == 0)
- {
- return null;
- }
- else
- {
- return errorLogs;
- }
}
- private static List CheckInternal(ModularAvatarBoneProxy bp)
+ private static void CheckInternal(ModularAvatarBoneProxy bp)
{
if (bp.target == null)
{
- return new List()
- {
- new ErrorLog(ReportLevel.Validation, "validation.bone_proxy.no_target", bp)
- };
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.bone_proxy.no_target", bp);
}
-
- return null;
}
#if MA_VRCSDK3_AVATARS
- private static List CheckInternal(ModularAvatarMenuInstaller mi)
+ private static void CheckInternal(ModularAvatarMenuInstaller mi)
{
// TODO - check that target menu is in the avatar
if (mi.menuToAppend == null && mi.GetComponent() == null)
{
- return new List()
- {
- new ErrorLog(ReportLevel.Validation, "validation.menu_installer.no_menu", mi)
- };
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.menu_installer.no_menu", mi);
}
-
- return null;
}
- private static List CheckInternal(ModularAvatarMergeAnimator ma)
+ private static void CheckInternal(ModularAvatarMergeAnimator ma)
{
if (ma.animator == null)
{
- return new List()
- {
- new ErrorLog(ReportLevel.Validation, "validation.merge_animator.no_animator", ma)
- };
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.merge_animator.no_animator", ma);
}
-
- return null;
}
#endif
- private static List CheckInternal(ModularAvatarMergeArmature ma)
+ private static void CheckInternal(ModularAvatarMergeArmature ma)
{
if (ma.mergeTargetObject == null)
{
- return new List()
- {
- new ErrorLog(ReportLevel.Validation, "validation.merge_armature.no_target", ma)
- };
+ BuildReport.Log(ErrorSeverity.NonFatal, "validation.merge_armature.no_target", ma);
+ return;
}
if (ma.mergeTargetObject == ma.gameObject || ma.mergeTargetObject.transform.IsChildOf(ma.transform))
{
- return new List()
- {
- new ErrorLog(ReportLevel.Validation, "error.merge_armature.merge_into_self", ma,
- ma.mergeTargetObject)
- };
+ BuildReport.Log(ErrorSeverity.Error, "error.merge_armature.circular_dependency", ma,
+ ma.mergeTargetObject);
}
-
- return null;
}
}
}
\ No newline at end of file
diff --git a/Editor/ErrorReporting/ErrorElement.cs b/Editor/ErrorReporting/ErrorElement.cs
deleted file mode 100644
index 045d4fe1..00000000
--- a/Editor/ErrorReporting/ErrorElement.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-using System;
-using nadena.dev.modular_avatar.core.editor;
-using UnityEditor;
-using UnityEngine;
-using UnityEngine.SceneManagement;
-using UnityEngine.UIElements;
-
-namespace nadena.dev.modular_avatar.editor.ErrorReporting
-{
- internal class ErrorElement : Box
- {
- private readonly ErrorLog log;
-
- Texture2D GetIcon()
- {
- switch (log.reportLevel)
- {
- case ReportLevel.Info:
- return EditorGUIUtility.FindTexture("d_console.infoicon");
- case ReportLevel.Warning:
- return EditorGUIUtility.FindTexture("d_console.warnicon");
- default:
- return EditorGUIUtility.FindTexture("d_console.erroricon");
- }
- }
-
- public ErrorElement(ErrorLog log, ObjectRefLookupCache cache)
- {
- this.log = log;
-
- AddToClassList("ErrorElement");
- var tex = GetIcon();
- if (tex != null)
- {
- var image = new Image();
- image.image = tex;
- Add(image);
- }
-
- var inner = new Box();
- Add(inner);
-
- var label = new Label(GetLabelText());
- inner.Add(label);
-
- foreach (var obj in log.referencedObjects)
- {
- var referenced = obj.Lookup(cache);
- if (referenced != null)
- {
- inner.Add(new SelectionButton(obj.typeName, referenced));
- }
- }
-
- if (!string.IsNullOrWhiteSpace(log.stacktrace))
- {
- var foldout = new Foldout();
- foldout.text = Localization.S("error.stack_trace");
- var field = new TextField();
- field.value = log.stacktrace;
- field.isReadOnly = true;
- field.multiline = true;
- foldout.Add(field);
- foldout.value = false;
- inner.Add(foldout);
- }
- }
-
- private static GameObject FindObject(string path)
- {
- var scene = SceneManager.GetActiveScene();
- foreach (var root in scene.GetRootGameObjects())
- {
- if (root.name == path) return root;
- if (path.StartsWith(root.name + "/"))
- {
- return root.transform.Find(path.Substring(root.name.Length + 1))?.gameObject;
- }
- }
-
- return null;
- }
-
- private string GetLabelText()
- {
- var objArray = new object[log.substitutions.Length];
- for (int i = 0; i < log.substitutions.Length; i++)
- {
- objArray[i] = log.substitutions[i];
- }
-
- try
- {
- return string.Format(Localization.S(log.messageCode), objArray);
- }
- catch (FormatException e)
- {
- Debug.LogError("Error formatting message code: " + log.messageCode);
- Debug.LogException(e);
- return log.messageCode + "\n" + string.Join("\n", objArray);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/Editor/ErrorReporting/ErrorElement.cs.meta b/Editor/ErrorReporting/ErrorElement.cs.meta
deleted file mode 100644
index 763f54eb..00000000
--- a/Editor/ErrorReporting/ErrorElement.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: a534edd7151c4cd49fe07919ae526004
-timeCreated: 1674132977
\ No newline at end of file
diff --git a/Editor/ErrorReporting/ErrorLog.cs b/Editor/ErrorReporting/ErrorLog.cs
index 52b279f9..2f068c18 100644
--- a/Editor/ErrorReporting/ErrorLog.cs
+++ b/Editor/ErrorReporting/ErrorLog.cs
@@ -3,6 +3,8 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using nadena.dev.modular_avatar.core;
+using nadena.dev.modular_avatar.core.editor;
+using nadena.dev.ndmf;
using Newtonsoft.Json;
using UnityEngine;
using UnityEditor;
@@ -11,376 +13,38 @@ using Object = UnityEngine.Object;
namespace nadena.dev.modular_avatar.editor.ErrorReporting
{
- internal class AvatarReport
- {
- [JsonProperty] internal ObjectRef objectRef;
-
- [JsonProperty] internal bool successful;
-
- [JsonProperty] internal List logs = new List();
- }
-
- internal class ObjectRefLookupCache
- {
- private Dictionary> _cache =
- new Dictionary>();
-
- internal UnityEngine.Object FindByGuidAndLocalId(string guid, long localId)
- {
- if (!_cache.TryGetValue(guid, out var fileContents))
- {
- var path = AssetDatabase.GUIDToAssetPath(guid);
- if (string.IsNullOrEmpty(path))
- {
- return null;
- }
-
- var assets = AssetDatabase.LoadAllAssetsAtPath(path);
- fileContents = new Dictionary(assets.Length);
- foreach (var asset in assets)
- {
- if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out var _, out long detectedId))
- {
- fileContents[detectedId] = asset;
- }
- }
-
- _cache[guid] = fileContents;
- }
-
- if (fileContents.TryGetValue(localId, out var obj))
- {
- return obj;
- }
- else
- {
- return null;
- }
- }
- }
-
- internal struct ObjectRef
- {
- [JsonProperty] internal string guid;
- [JsonProperty] internal long? localId;
- [JsonProperty] internal string path, name;
- [JsonProperty] internal string typeName;
-
- internal ObjectRef(Object obj)
- {
- this.guid = null;
- localId = null;
-
- if (obj == null)
- {
- this.guid = path = name = null;
- localId = null;
- typeName = null;
- return;
- }
-
- typeName = obj.GetType().Name;
-
- long id;
- if (AssetDatabase.TryGetGUIDAndLocalFileIdentifier(obj, out var guid, out id))
- {
- this.guid = guid;
- localId = id;
- }
-
- if (obj is Component c)
- {
- path = RuntimeUtil.RelativePath(null, c.gameObject);
- }
- else if (obj is GameObject go)
- {
- path = RuntimeUtil.RelativePath(null, go);
- }
- else
- {
- path = null;
- }
-
- name = string.IsNullOrWhiteSpace(obj.name) ? "??>" : obj.name;
- }
-
- internal UnityEngine.Object Lookup(ObjectRefLookupCache cache)
- {
- if (path != null)
- {
- return FindObject(path);
- }
- else if (guid != null && localId.HasValue)
- {
- return cache.FindByGuidAndLocalId(guid, localId.Value);
- }
- else
- {
- return null;
- }
- }
-
- private static GameObject FindObject(string path)
- {
- var scene = SceneManager.GetActiveScene();
- foreach (var root in scene.GetRootGameObjects())
- {
- if (root.name == path) return root;
- if (path.StartsWith(root.name + "/"))
- {
- return root.transform.Find(path.Substring(root.name.Length + 1))?.gameObject;
- }
- }
-
- return null;
- }
-
- public ObjectRef Remap(string original, string cloned)
- {
- if (path == cloned)
- {
- path = original;
- name = path.Substring(path.LastIndexOf('/') + 1);
- }
- else if (path != null && path.StartsWith(cloned + "/"))
- {
- path = original + path.Substring(cloned.Length);
- name = path.Substring(path.LastIndexOf('/') + 1);
- }
-
- return this;
- }
- }
-
- internal enum ReportLevel
- {
- Validation,
- Info,
- Warning,
- Error,
- InternalError,
- }
-
- internal class ErrorLog
- {
- [JsonProperty] internal List referencedObjects;
- [JsonProperty] internal ReportLevel reportLevel;
- [JsonProperty] internal string messageCode;
- [JsonProperty] internal string[] substitutions;
- [JsonProperty] internal string stacktrace;
-
- internal ErrorLog(ReportLevel level, string code, string[] strings, params object[] args)
- {
- reportLevel = level;
-
- substitutions = strings.Select(s => s.ToString()).ToArray();
-
- referencedObjects = args.Where(o => o is Component || o is GameObject)
- .Select(o => new ObjectRef(o is Component c ? c.gameObject : (GameObject) o))
- .ToList();
- referencedObjects.AddRange(BuildReport.CurrentReport.GetActiveReferences());
-
- messageCode = code;
- stacktrace = null;
- }
-
- internal ErrorLog(ReportLevel level, string code, params object[] args) : this(level, code,
- Array.Empty(), args)
- {
- }
-
- internal ErrorLog(Exception e, string additionalStackTrace = "")
- {
- reportLevel = ReportLevel.InternalError;
- messageCode = "error.internal_error";
- substitutions = new string[] {e.Message, e.TargetSite?.Name};
- referencedObjects = BuildReport.CurrentReport.GetActiveReferences().ToList();
- stacktrace = e.ToString() + additionalStackTrace;
- }
-
- public override string ToString()
- {
- return "[" + reportLevel + "] " + messageCode + " " + "subst: " + string.Join(", ", substitutions);
- }
- }
-
internal class BuildReport
{
private const string Path = "Library/ModularAvatarBuildReport.json";
- private static BuildReport _report;
- private AvatarReport _currentAvatar;
- private Stack _references = new Stack