using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using UnityEngine; using System.Reflection; namespace VRC.SDKBase.Validation { public static class ValidationUtils { public static void RemoveIllegalComponents(GameObject target, HashSet whitelist, bool retry = true, bool onlySceneObjects = false, bool logStripping = true) { List foundComponents = FindIllegalComponents(target, whitelist); foreach(Component component in foundComponents) { if(component == null) { continue; } if(onlySceneObjects && component.GetInstanceID() < 0) { continue; } if(logStripping) { Core.Logger.LogWarning($"Removing {component.GetType().Name} comp from {component.gameObject.name}"); } RemoveComponent(component); } } public static List FindIllegalComponents(GameObject target, HashSet whitelist) { List foundComponents = new List(); Component[] allComponents = target.GetComponentsInChildren(true); foreach(Component component in allComponents) { if(component == null) { continue; } Type componentType = component.GetType(); if(whitelist.Contains(componentType)) { continue; } foundComponents.Add(component); } return foundComponents; } private static readonly Dictionary _typeCache = new Dictionary(); private static readonly Dictionary> _whitelistCache = new Dictionary>(); public static HashSet WhitelistedTypes(string whitelistName, IEnumerable componentTypeWhitelist) { if (_whitelistCache.ContainsKey(whitelistName)) { return _whitelistCache[whitelistName]; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); HashSet whitelist = new HashSet(); foreach(string whitelistedTypeName in componentTypeWhitelist) { Type whitelistedType = GetTypeFromName(whitelistedTypeName, assemblies); if(whitelistedType == null) { continue; } if(whitelist.Contains(whitelistedType)) { continue; } whitelist.Add(whitelistedType); } AddDerivedClasses(whitelist); _whitelistCache[whitelistName] = whitelist; return _whitelistCache[whitelistName]; } public static HashSet WhitelistedTypes(string whitelistName, IEnumerable componentTypeWhitelist) { if (_whitelistCache.ContainsKey(whitelistName)) { return _whitelistCache[whitelistName]; } HashSet whitelist = new HashSet(); whitelist.UnionWith(componentTypeWhitelist); AddDerivedClasses(whitelist); _whitelistCache[whitelistName] = whitelist; return _whitelistCache[whitelistName]; } private static void AddDerivedClasses(HashSet whitelist) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach(Assembly assembly in assemblies) { foreach(Type type in assembly.GetTypes()) { if(whitelist.Contains(type)) { continue; } if(!typeof(Component).IsAssignableFrom(type)) { continue; } Type currentType = type; while(currentType != typeof(object) && currentType != null) { if(whitelist.Contains(currentType)) { whitelist.Add(type); break; } currentType = currentType.BaseType; } } } } public static Type GetTypeFromName(string name, Assembly[] assemblies = null) { if (_typeCache.ContainsKey(name)) { return _typeCache[name]; } if (assemblies == null) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); } foreach (Assembly assembly in assemblies) { Type found = assembly.GetType(name); if(found == null) { continue; } _typeCache[name] = found; return found; } //This is really verbose for some SDK scenes, eg. //If they don't have FinalIK installed #if VRC_CLIENT && UNITY_EDITOR Debug.LogWarningFormat("Could not find type {0}", name); #endif _typeCache[name] = null; return null; } private static readonly Dictionary> _requireComponentsCache = new Dictionary>(); private static void RemoveDependencies(Component rootComponent) { if (rootComponent == null) { return; } Component[] components = rootComponent.GetComponents(); if (components == null || components.Length == 0) { return; } Type compType = rootComponent.GetType(); foreach (var siblingComponent in components) { if (siblingComponent == null) { continue; } Type siblingComponentType = siblingComponent.GetType(); if(!_requireComponentsCache.TryGetValue(siblingComponentType, out ImmutableArray requiredComponentAttributes)) { requiredComponentAttributes = siblingComponentType.GetCustomAttributes(typeof(RequireComponent), true).Cast().ToImmutableArray(); _requireComponentsCache.Add(siblingComponentType, requiredComponentAttributes); } bool deleteMe = false; foreach (RequireComponent requireComponent in requiredComponentAttributes) { if (requireComponent == null) { continue; } if(requireComponent.m_Type0 != compType && requireComponent.m_Type1 != compType && requireComponent.m_Type2 != compType) { continue; } deleteMe = true; break; } if (deleteMe && siblingComponent != rootComponent) { RemoveComponent(siblingComponent); } } } public static void RemoveComponent(Component comp) { if (comp == null) { return; } RemoveDependencies(comp); #if VRC_CLIENT UnityEngine.Object.DestroyImmediate(comp, true); #else UnityEngine.Object.DestroyImmediate(comp, false); #endif } public static void RemoveComponentsOfType(GameObject target) where T : Component { if (target == null) return; foreach (T comp in target.GetComponentsInChildren(true)) { if (comp == null || comp.gameObject == null) continue; RemoveComponent(comp); } } } }