allow components off root, but only allow one or error

This commit is contained in:
Lillith Rose 2026-01-07 16:52:44 -05:00
parent a49d2d74e6
commit 1f779f9297
18 changed files with 133 additions and 12 deletions

View file

@ -2,6 +2,7 @@ using System.Linq;
using System.Diagnostics;
using AnimatorAsCode.V1;
using gay.lilyy.aaccore;
using gay.lilyy.Common;
using nadena.dev.ndmf;
using AnimatorAsCode.V1.ModularAvatar;
using UnityEditor;
@ -164,7 +165,7 @@ namespace gay.lilyy.aaccore
var overallStopwatch = Stopwatch.StartNew();
V5AACLogger.LogInfo("Starting Lillith V5 AAC generation...");
var root = ctx.AvatarRootObject.GetComponent<AACRoot>();
var root = ComponentHelper.GetComponentInChildrenWithError<AACRoot>(ctx.AvatarRootObject);
if (root == null)
{
V5AACLogger.LogInfo("No LillithV5AACRoot component found. Skipping.");

View file

@ -11,7 +11,8 @@
"GUID:901e56b065a857d4483a77f8cae73588",
"GUID:04a7e5cf006503242b1db329a84d694d",
"GUID:95124d49b8c897e4286f0bf6c6e57f4d",
"GUID:a65a5779a3702144986d83fca255f5da"
"GUID:a65a5779a3702144986d83fca255f5da",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -11,7 +11,8 @@
"GUID:04a7e5cf006503242b1db329a84d694d",
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:e73da13578f7b4d4fa785d6f8fe72ba3",
"GUID:fc900867c0f47cd49b6e2ae4ef907300"
"GUID:fc900867c0f47cd49b6e2ae4ef907300",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -4,6 +4,7 @@ using AnimatorAsCode.V1;
using System.Collections.Generic;
using gay.lilyy.aaccore;
using gay.lilyy.aacshared.runtimecomponents;
using gay.lilyy.Common;
namespace gay.lilyy.aacshared.layers
{
@ -30,7 +31,7 @@ namespace gay.lilyy.aacshared.layers
public override bool IsApplicable(AACAssets assets)
{
var definition = assets.ctx.AvatarRootObject.GetComponent<LanternFlickerDefinition>();
var definition = ComponentHelper.GetComponentInChildrenWithError<LanternFlickerDefinition>(assets.ctx.AvatarRootObject);
if (definition == null) return false;
if (!definition.enabled) return false;
var lamp = GetLantern(assets);

8
Common.meta Normal file
View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 1e2dd3118413d6c43a259b24869f8a9a
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

8
Common/Runtime.meta Normal file
View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 0c8226e52ebf1fe47b6aea755b9d9bd7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,15 @@
{
"name": "CommonRuntime",
"rootNamespace": "",
"references": [],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
"defineConstraints": [],
"versionDefines": [],
"noEngineReferences": false
}

View file

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 209cbd2a789c4f72963fdbf1f8a01909
AssemblyDefinitionImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -0,0 +1,58 @@
using UnityEngine;
namespace gay.lilyy.Common
{
public static class ComponentHelper
{
/// <summary>
/// Searches for a component in children of the root GameObject.
/// Returns null if no component is found.
/// Logs an error and returns null if multiple components are found.
/// </summary>
/// <typeparam name="T">The component type to search for</typeparam>
/// <param name="root">The root GameObject to search in</param>
/// <returns>The component if exactly one is found, null otherwise</returns>
public static T? GetComponentInChildrenWithError<T>(GameObject root) where T : Component
{
if (root == null) return null;
T[] components = root.GetComponentsInChildren<T>(true);
if (components.Length == 0)
{
return null;
}
if (components.Length > 1)
{
Debug.LogError($"Multiple {typeof(T).Name} components found in children of {root.name}. Only one is allowed.");
return null;
}
return components[0];
}
/// <summary>
/// Searches for a component in children of the root Transform.
/// Returns null if no component is found.
/// Logs an error and returns null if multiple components are found.
/// </summary>
/// <typeparam name="T">The component type to search for</typeparam>
/// <param name="root">The root Transform to search in</param>
/// <returns>The component if exactly one is found, null otherwise</returns>
public static T? GetComponentInChildrenWithError<T>(Transform root) where T : Component
{
if (root == null) return null;
T[] components = root.GetComponentsInChildren<T>(true);
if (components.Length == 0)
{
return null;
}
if (components.Length > 1)
{
Debug.LogError($"Multiple {typeof(T).Name} components found in children of {root.name}. Only one is allowed.");
return null;
}
return components[0];
}
}
}

View file

@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 115183d8dc354ef7b1c43fb49aeaab2f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View file

@ -5,7 +5,8 @@
"GUID:4713dd3cf2e7fa34cbc6bbb6fef3c7d0",
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:5718fb738711cd34ea54e9553040911d",
"GUID:901e56b065a857d4483a77f8cae73588"
"GUID:901e56b065a857d4483a77f8cae73588",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using gay.lilyy.Common;
using gay.lilyy.MenuIconRemover;
using nadena.dev.ndmf;
using nadena.dev.ndmf.vrchat;
@ -20,7 +21,7 @@ namespace gay.lilyy.MenuIconRemover
{
InPhase(BuildPhase.Optimizing).BeforePlugin("gay.lilyy.MenuStyling").Run("RemoveMenuIcons", ctx =>
{
var obj = ctx.AvatarRootObject.GetComponent<MenuIconRemoverConfig>();
var obj = ComponentHelper.GetComponentInChildrenWithError<MenuIconRemoverConfig>(ctx.AvatarRootObject);
if (obj != null)
{
var shouldRemove = false;

View file

@ -5,7 +5,8 @@
"GUID:108c6ed81f83e074fa168f7087c2a246",
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:5718fb738711cd34ea54e9553040911d",
"GUID:901e56b065a857d4483a77f8cae73588"
"GUID:901e56b065a857d4483a77f8cae73588",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using gay.lilyy.Common;
using gay.lilyy.MenuIconReplacer;
using nadena.dev.ndmf;
using nadena.dev.ndmf.vrchat;
@ -21,7 +22,7 @@ namespace gay.lilyy.MenuIconReplacer
{
InPhase(BuildPhase.Optimizing).BeforePlugin("gay.lilyy.MenuIconRemover").Run("ReplaceMenuIcons", ctx =>
{
var obj = ctx.AvatarRootObject.GetComponent<MenuIconReplacerConfig>();
var obj = ComponentHelper.GetComponentInChildrenWithError<MenuIconReplacerConfig>(ctx.AvatarRootObject);
if (obj != null)
{
var descriptor = ctx.VRChatAvatarDescriptor();

View file

@ -5,7 +5,8 @@
"GUID:8a060350c9ddd424197c8fd1e0d63f72",
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:5718fb738711cd34ea54e9553040911d",
"GUID:901e56b065a857d4483a77f8cae73588"
"GUID:901e56b065a857d4483a77f8cae73588",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Text.RegularExpressions;
using gay.lilyy.Common;
using gay.lilyy.MenuStyling;
using nadena.dev.ndmf;
using nadena.dev.ndmf.vrchat;
@ -19,7 +20,7 @@ namespace gay.lilyy.MenuStyling
{
InPhase(BuildPhase.Optimizing).AfterPlugin("gay.lilyy.MenuIconRemover").Run("SetMenuStyling", ctx =>
{
var obj = ctx.AvatarRootObject.GetComponent<MenuStylingConfig>();
var obj = ComponentHelper.GetComponentInChildrenWithError<MenuStylingConfig>(ctx.AvatarRootObject);
if (obj != null)
{
var descriptor = ctx.VRChatAvatarDescriptor();

View file

@ -4,7 +4,8 @@
"references": [
"GUID:23e6cebda363f004aa6e529a95f8a6fc",
"GUID:62ced99b048af7f4d8dfe4bed8373d76",
"GUID:5718fb738711cd34ea54e9553040911d"
"GUID:5718fb738711cd34ea54e9553040911d",
"GUID:209cbd2a789c4f72963fdbf1f8a01909"
],
"includePlatforms": [
"Editor"

View file

@ -1,3 +1,4 @@
using gay.lilyy.Common;
using gay.lilyy.MeshCompression;
using nadena.dev.ndmf;
using UnityEditor;
@ -29,7 +30,7 @@ namespace gay.lilyy.MeshCompression
InPhase(BuildPhase.Optimizing)
.Run("Set Mesh Compression", ctx =>
{
var defaultConfig = ctx.AvatarRootObject.GetComponent<MeshCompressionConfig>();
var defaultConfig = ComponentHelper.GetComponentInChildrenWithError<MeshCompressionConfig>(ctx.AvatarRootObject);
var renderers = ctx.AvatarRootObject.GetComponentsInChildren<Renderer>(true);
foreach (var renderer in renderers)