diff options
| author | yum <yum.food.vr@gmail.com> | 2025-08-18 18:07:25 -0700 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2025-08-18 18:14:32 -0700 |
| commit | ce52a617927f9fd778ca48c27151a23dc4a3fc31 (patch) | |
| tree | c361c94bf5aedd6fd4adbfeaaea9c358fbc2a282 /Scripts | |
| parent | b6eaff0fba81d952d9002cb04dca9bd5887d3757 (diff) | |
Improve error reporting
Diffstat (limited to 'Scripts')
| -rw-r--r-- | Scripts/YOTSConfig.cs (renamed from Scripts/YOTSNDMFConfig.cs) | 284 | ||||
| -rw-r--r-- | Scripts/YOTSCore.cs | 7 | ||||
| -rw-r--r-- | Scripts/YOTSNDMFGenerator.cs | 123 |
3 files changed, 201 insertions, 213 deletions
diff --git a/Scripts/YOTSNDMFConfig.cs b/Scripts/YOTSConfig.cs index a1d8bad..a6a3f9c 100644 --- a/Scripts/YOTSNDMFConfig.cs +++ b/Scripts/YOTSConfig.cs @@ -1,142 +1,142 @@ -#if UNITY_EDITOR
-
-using UnityEngine;
-using nadena.dev.ndmf;
-using VRC.SDK3.Avatars.ScriptableObjects;
-using UnityEditor;
-using System.IO;
-
-namespace YOTS
-{
- [DisallowMultipleComponent]
- [AddComponentMenu("YOTS NDMF Config")]
- public class YOTSNDMFConfig : MonoBehaviour {
- [Tooltip("The JSON configuration file.")]
- public TextAsset jsonConfig;
-
- [TextArea(5, 80)] // Min 5 lines, max 80 lines
- public string jsonContent;
-
- [SerializeField, HideInInspector]
- private TextAsset lastJsonConfig;
-
- void OnValidate() {
- gameObject.tag = "EditorOnly";
-
- // Only update jsonContent when jsonConfig actually changes
- if (jsonConfig != lastJsonConfig) {
- if (jsonConfig != null) {
- jsonContent = jsonConfig.text;
- }
- lastJsonConfig = jsonConfig;
- }
- }
- }
-
- [CustomEditor(typeof(YOTSNDMFConfig))]
- public class YOTSNDMFConfigEditor : Editor
- {
- private YOTSNDMFConfig config;
-
- private void OnEnable()
- {
- config = (YOTSNDMFConfig)target;
- }
-
- public override void OnInspectorGUI()
- {
- EditorGUI.BeginChangeCheck();
-
- // Draw the default inspector
- DrawDefaultInspector();
-
- EditorGUILayout.HelpBox(
- "You can inspect and edit the JSON config using the textbox above. " +
- "Changes are saved automatically. Changes from external editors will " +
- "appear upon tabbing back into Unity.",
- MessageType.Info);
-
-
- // If changes were made in the inspector
- if (EditorGUI.EndChangeCheck())
- {
- // Save changes immediately
- SaveJsonToFile();
- }
- // Only check for file changes if we're not currently editing
- else if (config.jsonConfig != null)
- {
- string currentContent = config.jsonConfig.text;
- if (currentContent != config.jsonContent)
- {
- config.jsonContent = currentContent;
- GUI.changed = true;
- }
- }
-
- // Check for Ctrl+S
- Event e = Event.current;
- if (e.type == EventType.KeyDown && e.keyCode == KeyCode.S && e.control)
- {
- e.Use();
- SaveJsonToFile();
- }
- }
-
- private void SaveJsonToFile()
- {
- if (config.jsonConfig == null)
- {
- Debug.LogWarning("No JSON config file assigned!");
- return;
- }
-
- string assetPath = AssetDatabase.GetAssetPath(config.jsonConfig);
- if (string.IsNullOrEmpty(assetPath))
- {
- Debug.LogError("Could not find asset path!");
- return;
- }
-
- try
- {
- // Write the modified content from our component
- File.WriteAllText(assetPath, config.jsonContent);
-
- // Force Unity to reload the file from disk
- AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate);
-
- // Update the TextAsset to match our changes
- var serializedObject = new SerializedObject(config.jsonConfig);
- serializedObject.FindProperty("m_Script").stringValue = config.jsonContent;
- serializedObject.ApplyModifiedProperties();
-
- EditorUtility.SetDirty(config.jsonConfig);
- AssetDatabase.SaveAssets();
- Debug.Log($"Successfully saved JSON to {assetPath}");
- }
- catch (System.Exception ex)
- {
- Debug.LogError($"Error saving JSON: {ex.Message}");
- }
- }
- }
-
- // Add this new class to handle file modifications
- public class JsonFileProcessor : UnityEditor.AssetModificationProcessor
- {
- private static void OnWillSaveAssets(string[] paths)
- {
- foreach (string path in paths)
- {
- if (path.EndsWith(".json"))
- {
- // Force Unity to reimport the asset
- AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
- }
- }
- }
- }
-}
-
-#endif // UNITY_EDITOR
+#if UNITY_EDITOR + +using UnityEngine; +using VRC.SDK3.Avatars.ScriptableObjects; +using UnityEditor; +using System.IO; + +namespace YOTS +{ + [DisallowMultipleComponent] + [AddComponentMenu("YOTS Config")] + public class YOTSConfig : MonoBehaviour { + [Tooltip("The JSON configuration file.")] + public TextAsset jsonConfig; + + [TextArea(5, 30)] + public string jsonContent; + + [SerializeField, HideInInspector] + private TextAsset lastJsonConfig; + + void OnValidate() { + gameObject.tag = "EditorOnly"; + + // Only update jsonContent when jsonConfig actually changes + if (jsonConfig != lastJsonConfig) { + if (jsonConfig != null) { + jsonContent = jsonConfig.text; + } + lastJsonConfig = jsonConfig; + } + } + } + + [CustomEditor(typeof(YOTSConfig))] + public class YOTSConfigEditor : Editor + { + private YOTSConfig config; + + private void OnEnable() + { + config = (YOTSConfig)target; + } + + public override void OnInspectorGUI() + { + EditorGUI.BeginChangeCheck(); + + // Draw the default inspector + DrawDefaultInspector(); + + EditorGUILayout.HelpBox( + "You can inspect and edit the JSON config using the textbox above. " + + "Changes are saved automatically. Changes from external editors will " + + "appear upon tabbing back into Unity.", + MessageType.Info); + + + // If changes were made in the inspector + if (EditorGUI.EndChangeCheck()) + { + // Save changes immediately + SaveJsonToFile(); + } + // Only check for file changes if we're not currently editing + else if (config.jsonConfig != null) + { + string currentContent = config.jsonConfig.text; + if (currentContent != config.jsonContent) + { + config.jsonContent = currentContent; + GUI.changed = true; + } + } + + // Check for Ctrl+S + Event e = Event.current; + if (e.type == EventType.KeyDown && e.keyCode == KeyCode.S && e.control) + { + e.Use(); + SaveJsonToFile(); + } + } + + private void SaveJsonToFile() + { + if (config.jsonConfig == null) + { + Debug.LogWarning("No JSON config file assigned!"); + return; + } + + string assetPath = AssetDatabase.GetAssetPath(config.jsonConfig); + if (string.IsNullOrEmpty(assetPath)) + { + Debug.LogError("Could not find asset path!"); + return; + } + + try + { + // Write the modified content from our component + File.WriteAllText(assetPath, config.jsonContent); + + // Force Unity to reload the file from disk + AssetDatabase.ImportAsset(assetPath, ImportAssetOptions.ForceUpdate); + + // Update the TextAsset to match our changes + var serializedObject = new SerializedObject(config.jsonConfig); + serializedObject.FindProperty("m_Script").stringValue = config.jsonContent; + serializedObject.ApplyModifiedProperties(); + + EditorUtility.SetDirty(config.jsonConfig); + AssetDatabase.SaveAssets(); + Debug.Log($"Successfully saved JSON to {assetPath}"); + } + catch (System.Exception ex) + { + Debug.LogError($"Error saving JSON: {ex.Message}"); + } + } + } + + // Add this new class to handle file modifications + public class JsonFileProcessor : UnityEditor.AssetModificationProcessor + { + private static void OnWillSaveAssets(string[] paths) + { + foreach (string path in paths) + { + if (path.EndsWith(".json")) + { + // Force Unity to reimport the asset + AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate); + } + } + } + } +} + +#endif // UNITY_EDITOR + diff --git a/Scripts/YOTSCore.cs b/Scripts/YOTSCore.cs index 49f54d7..d76f382 100644 --- a/Scripts/YOTSCore.cs +++ b/Scripts/YOTSCore.cs @@ -673,7 +673,7 @@ namespace YOTS // Find the toggle with this dependency name
var depToggle = toggleSpecs.FirstOrDefault(t => t.name == dep);
if (depToggle == null) {
- throw new System.Exception($"Toggle '{toggle.name}' has dependency '{dep}' that doesn't exist");
+ throw new ArgumentException($"Toggle '{toggle.name}' has dependency '{dep}' that doesn't exist");
}
string depParamName = depToggle.GetParameterName();
if (!graph.ContainsKey(depParamName))
@@ -731,9 +731,10 @@ namespace YOTS // Provide detailed error message
if (cycleNodes.Count == 0) {
- throw new System.Exception($"Dependency cycle detected but couldn't identify specific nodes. Unprocessed parameters: {string.Join(", ", unprocessedParams)}");
+ // This should never happen.
+ throw new ArgumentException($"Dependency cycle detected but couldn't identify specific nodes. Unprocessed parameters: {string.Join(", ", unprocessedParams)}");
} else {
- throw new System.Exception($"Dependency cycle detected in toggle specifications. Nodes involved: {string.Join(", ", cycleNodes)}");
+ throw new ArgumentException($"Dependency cycle detected in toggle specifications. Nodes involved: {string.Join(", ", cycleNodes)}");
}
}
diff --git a/Scripts/YOTSNDMFGenerator.cs b/Scripts/YOTSNDMFGenerator.cs index aa8ef84..f8cf889 100644 --- a/Scripts/YOTSNDMFGenerator.cs +++ b/Scripts/YOTSNDMFGenerator.cs @@ -17,18 +17,30 @@ using UnityEditor.Animations; namespace YOTS
{
public class YOTSNDMFGenerator : Plugin<YOTSNDMFGenerator> {
- private readonly Localizer localizer = new Localizer("en-us", () =>
+ private readonly Localizer lcl = new Localizer("en-us", () =>
new List<(string, Func<string, string>)> {
- ("en-us", key => key)
+ ("en-us", key => {
+ switch (key) {
+ case "json_missing": return "YOTS configuration JSON file is missing";
+ case "config_missing": return "YOTS config component not found on avatar";
+ case "descriptor_missing": return "VRC Avatar Descriptor is missing from avatar";
+ case "expressions_missing": return "Avatar is missing Expression Parameters or Expression Menu";
+ case "param_exists": return "Parameter '{0}' already exists in FX animator";
+ case "layer_exists": return "Layer '{0}' already exists in FX animator";
+ case "config_error": return "{0}";
+ default: return null;
+ }
+ })
});
public override string DisplayName => "YOTS Animator Generator";
protected override void Configure() {
- // First pass: Retrieve and stash configuration
+ // First pass: Retrieve and stash configuration. By the time we're in the
+ // Transforming phase, we can no longer access the YOTSConfig object.
InPhase(BuildPhase.Resolving)
.Run("Cache YOTS Config", ctx => {
- var config = ctx.AvatarRootObject.GetComponentInChildren<YOTSNDMFConfig>();
+ var config = ctx.AvatarRootObject.GetComponentInChildren<YOTSConfig>();
if (config == null) {
ctx.GetState<YOTSBuildState>().skipGeneration = true;
Debug.Log("No YOTS config found - skipping.");
@@ -36,12 +48,8 @@ namespace YOTS }
if (config.jsonConfig == null) {
ctx.GetState<YOTSBuildState>().skipGeneration = true;
- ErrorReport.WithContextObject(ctx.AvatarRootObject, () => {
- ErrorReport.ReportException(
- new Exception("No YOTS config found"),
- "Missing required YOTS configuration"
- );
- });
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "json_missing",
+ ctx.AvatarRootObject);
return;
}
ctx.GetState<YOTSBuildState>().jsonConfig = config.jsonConfig.text;
@@ -57,45 +65,29 @@ namespace YOTS return;
}
if (config == null) {
- ErrorReport.WithContextObject(ctx.AvatarRootObject, () => {
- ErrorReport.ReportException(
- new Exception("No YOTS config component found"),
- "Missing required YOTS configuration"
- );
- });
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "config_missing",
+ ctx.AvatarRootObject);
return;
}
if (config.jsonConfig == null) {
- ErrorReport.WithContextObject(ctx.AvatarRootObject, () => {
- ErrorReport.ReportException(
- new Exception("Missing JSON config file"),
- "YOTS config component is missing required JSON configuration"
- );
- });
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "json_missing",
+ ctx.AvatarRootObject);
return;
}
// Get menu and parameters
var descriptor = ctx.AvatarDescriptor;
if (descriptor == null) {
- ErrorReport.WithContextObject(ctx.AvatarRootObject, () => {
- ErrorReport.ReportException(
- new Exception("Avatar descriptor is missing"),
- "Cannot find VRC Avatar Descriptor"
- );
- });
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "descriptor_missing",
+ ctx.AvatarRootObject);
return;
}
RuntimeAnimatorController originalAnimator = descriptor.baseAnimationLayers[4].animatorController;
var menu = descriptor.expressionsMenu;
var parameters = descriptor.expressionParameters;
if (parameters == null || menu == null) {
- ErrorReport.WithContextObject(descriptor, () => {
- ErrorReport.ReportException(
- new Exception("Missing required VRC assets"),
- "Avatar is missing required Expression Parameters or Menu"
- );
- });
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "expressions_missing",
+ descriptor);
return;
}
// Create copies so the originals don't get modified
@@ -105,18 +97,19 @@ namespace YOTS descriptor.expressionParameters = parameters;
// Generate the YOTS animator.
- RuntimeAnimatorController generatedAnimator = YOTSCore.GenerateAnimator(
- config.jsonConfig,
- parameters,
- menu
- );
- if (generatedAnimator == null) {
- ErrorReport.WithContextObject(ctx.AvatarRootObject, () => {
- ErrorReport.ReportException(
- new Exception("Failed to generate animator"),
- "YOTS animator generation failed"
- );
- });
+ RuntimeAnimatorController generatedAnimator = null;
+ try {
+ generatedAnimator = YOTSCore.GenerateAnimator(
+ config.jsonConfig,
+ parameters,
+ menu
+ );
+ } catch (ArgumentException e) {
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error, "config_error",
+ e.Message, ctx.AvatarRootObject);
+ return;
+ } catch (Exception e) {
+ ErrorReport.ReportException(e);
return;
}
@@ -128,39 +121,33 @@ namespace YOTS // Else append the generated animator to the original.
AnimatorController originalController = originalAnimator as AnimatorController;
AnimatorController generatedController = generatedAnimator as AnimatorController;
- MergeAnimatorControllers(localizer, generatedController, originalController);
+ MergeAnimatorControllers(originalController, generatedController);
descriptor.baseAnimationLayers[4].animatorController = generatedController;
});
}
// Simply append generated params and layers to the original animator.
- private static void MergeAnimatorControllers(Localizer localizer, AnimatorController original, AnimatorController generated) {
- // Merge parameters from generated into original.
- foreach (var genParam in generated.parameters) {
+ private void MergeAnimatorControllers(AnimatorController from, AnimatorController to) {
+ // Merge parameters from from into to.
+ foreach (var genParam in from.parameters) {
// This is an O(m*n) check but m and n should be small enough to not matter.
- if (original.parameters.Any(p => p.name == genParam.name)) {
- ErrorReport.WithContextObject(original, () => {
- ErrorReport.ReportException(
- new Exception($"Parameter '{genParam.name}' already exists"),
- "Parameter name conflict in animator"
- );
- });
+ if (to.parameters.Any(p => p.name == genParam.name)) {
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error,
+ "param_exists",
+ genParam.name, to);
return;
}
- original.AddParameter(genParam);
+ to.AddParameter(genParam);
}
- // Append each YOTS layer after the original layers.
- foreach (var genLayer in generated.layers) {
+ // Append each YOTS layer after the to layers.
+ foreach (var genLayer in from.layers) {
// This isn't strictly an error but if someone already has layers named
// YOTS_* that's probably not on purpose.
- if (original.layers.Any(l => l.name == genLayer.name)) {
- ErrorReport.WithContextObject(original, () => {
- ErrorReport.ReportException(
- new Exception($"Layer '{genLayer.name}' already exists"),
- "Layer name conflict in animator"
- );
- });
+ if (to.layers.Any(l => l.name == genLayer.name)) {
+ ErrorReport.ReportError(lcl, ErrorSeverity.Error,
+ "layer_exists",
+ genLayer.name, to);
return;
}
var newLayer = new AnimatorControllerLayer {
@@ -168,7 +155,7 @@ namespace YOTS defaultWeight = genLayer.defaultWeight,
stateMachine = genLayer.stateMachine
};
- original.AddLayer(newLayer);
+ to.AddLayer(newLayer);
}
}
|
