Files

244 lines
7.5 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
namespace VRCSDK2.Validation
{
public static class ValidationUtils
{
public static void RemoveIllegalComponents(GameObject target, HashSet<Type> whitelist, bool retry = true, bool onlySceneObjects = false, bool logStripping = true)
{
IEnumerable<Component> foundComponents = FindIllegalComponents(target, whitelist);
foreach(Component component in foundComponents)
{
if(component == null)
{
continue;
}
if (onlySceneObjects && component.GetInstanceID() < 0)
{
continue;
}
if (logStripping)
{
VRC.Core.Logger.LogWarning(string.Format("Removing {0} comp from {1}", component.GetType().Name, component.gameObject.name));
}
RemoveComponent(component);
}
}
public static IEnumerable<Component> FindIllegalComponents(GameObject target, HashSet<Type> whitelist)
{
List<Component> foundComponents = new List<Component>();
Component[] allComponents = target.GetComponentsInChildren<Component>(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<string, Type> _typeCache = new Dictionary<string, Type>();
private static readonly Dictionary<string, HashSet<Type>> _whitelistCache = new Dictionary<string, HashSet<Type>>();
public static HashSet<Type> WhitelistedTypes(string whitelistName, IEnumerable<string> componentTypeWhitelist)
{
if (_whitelistCache.ContainsKey(whitelistName))
{
return _whitelistCache[whitelistName];
}
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
HashSet<Type> whitelist = new HashSet<Type>();
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<Type> WhitelistedTypes(string whitelistName, IEnumerable<Type> componentTypeWhitelist)
{
if (_whitelistCache.ContainsKey(whitelistName))
{
return _whitelistCache[whitelistName];
}
HashSet<Type> whitelist = new HashSet<Type>();
whitelist.UnionWith(componentTypeWhitelist);
AddDerivedClasses(whitelist);
_whitelistCache[whitelistName] = whitelist;
return _whitelistCache[whitelistName];
}
private static void AddDerivedClasses(HashSet<Type> 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 void RemoveDependencies(Component component)
{
if (component == null)
return;
Component[] components = component.GetComponents<Component>();
if (components == null || components.Length == 0)
return;
System.Type compType = component.GetType();
foreach (var c in components)
{
if (c == null)
continue;
bool deleteMe = false;
object[] requires = c.GetType().GetCustomAttributes(typeof(RequireComponent), true);
if (requires == null || requires.Length == 0)
continue;
foreach (var r in requires)
{
RequireComponent rc = r as RequireComponent;
if (rc == null)
continue;
if (rc.m_Type0 == compType ||
rc.m_Type1 == compType ||
rc.m_Type2 == compType)
{
deleteMe = true;
break;
}
}
if (deleteMe)
RemoveComponent(c);
}
}
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<T>(GameObject target) where T : Component
{
if (target == null)
return;
foreach (T comp in target.GetComponentsInChildren<T>(true))
{
if (comp == null || comp.gameObject == null)
continue;
RemoveComponent(comp);
}
}
}
}