Add VRChat and GitHub packages to Unity project

This commit is contained in:
gunnbr committed 2021-01-18 09:14:18 -08:00
1 parent 0334a52b21
commit 85538aaccc
3297 files changed
+847877

No files matched your search

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 38bda3f3d7ca8b9448473cf4c8efd08d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,167 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance
{
public static class AvatarPerformance
{
#region Public Constants
public const int DEFAULT_DYNAMIC_BONE_MAX_SIMULATED_BONE_LIMIT = 32;
public const int DEFAULT_DYNAMIC_BONE_MAX_COLLIDER_CHECK_LIMIT = 8;
#if UNITY_ANDROID || UNITY_IOS
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_DEFAULT = PerformanceRating.Medium;
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MIN = PerformanceRating.Medium;
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MAX = PerformanceRating.Poor;
#else
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_DEFAULT = PerformanceRating.VeryPoor;
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MIN = PerformanceRating.Medium;
internal const PerformanceRating AVATAR_PERFORMANCE_RATING_MINIMUM_TO_DISPLAY_MAX = PerformanceRating.VeryPoor;
#endif
#endregion
#region Public Delegates
public delegate bool IgnoreDelegate(Component component);
public delegate void FilterBlockCallback();
public static IgnoreDelegate ShouldIgnoreComponent { get; set; }
#endregion
#region Public Methods
public static void CalculatePerformanceStats(string avatarName, GameObject avatarObject, AvatarPerformanceStats perfStats)
{
perfStats.Reset();
perfStats.avatarName = avatarName;
PerformanceScannerSet performanceScannerSet = GetPerformanceScannerSet();
if(performanceScannerSet != null)
{
performanceScannerSet.RunPerformanceScan(avatarObject, perfStats, ShouldIgnoreComponentInternal);
}
// cache performance ratings
perfStats.CalculateAllPerformanceRatings();
}
public static IEnumerator CalculatePerformanceStatsEnumerator(string avatarName, GameObject avatarObject, AvatarPerformanceStats perfStats)
{
perfStats.Reset();
perfStats.avatarName = avatarName;
PerformanceScannerSet performanceScannerSet = GetPerformanceScannerSet();
if(performanceScannerSet != null)
{
yield return performanceScannerSet.RunPerformanceScanEnumerator(avatarObject, perfStats, ShouldIgnoreComponentInternal);
}
// cache performance ratings
perfStats.CalculateAllPerformanceRatings();
}
public static IEnumerator ApplyPerformanceFiltersEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, PerformanceRating minPerfRating, FilterBlockCallback onBlock)
{
// Performance Filtering is disabled.
if(minPerfRating == PerformanceRating.None)
{
yield break;
}
PerformanceFilterSet performanceFilterSet = GetPerformanceFilterSet();
if(performanceFilterSet == null)
{
yield break;
}
bool avatarBlocked = false;
yield return performanceFilterSet.ApplyPerformanceFilters(
avatarObject,
perfStats,
minPerfRating,
ShouldIgnoreComponentInternal,
() => { avatarBlocked = true; }
);
if(!avatarBlocked)
{
yield break;
}
VRC.Core.Logger.LogFormat(
"Avatar hidden due to low performance rating: [{0}] {1} - minimum setting: {2}",
perfStats.avatarName,
perfStats.GetPerformanceRatingForCategory(AvatarPerformanceCategory.Overall),
minPerfRating
);
onBlock();
}
#endregion
#region Private Methods
private static PerformanceScannerSet GetPerformanceScannerSet()
{
PerformanceScannerSet performanceScannerSet;
if(VRC.ValidationHelpers.IsStandalonePlatform())
{
performanceScannerSet = Resources.Load<PerformanceScannerSet>("Validation/Performance/ScannerSets/PerformanceScannerSet_Windows");
}
else
{
performanceScannerSet = Resources.Load<PerformanceScannerSet>("Validation/Performance/ScannerSets/PerformanceScannerSet_Quest");
}
return performanceScannerSet;
}
private static PerformanceFilterSet GetPerformanceFilterSet()
{
PerformanceFilterSet performanceFilterSet;
if(VRC.ValidationHelpers.IsStandalonePlatform())
{
performanceFilterSet = Resources.Load<PerformanceFilterSet>("Validation/Performance/FilterSets/PerformanceFilterSet_Windows");
}
else
{
performanceFilterSet = Resources.Load<PerformanceFilterSet>("Validation/Performance/FilterSets/PerformanceFilterSet_Quest");
}
return performanceFilterSet;
}
private static bool ShouldIgnoreComponentInternal(Component component)
{
if(Application.isEditor)
{
if(component == null)
{
return false;
}
if(component.CompareTag("EditorOnly"))
{
return true;
}
}
if(ShouldIgnoreComponent != null)
{
return ShouldIgnoreComponent(component);
}
return false;
}
#endregion
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 15ecac6f7fdc1bc4fb723fee6f4635dd
timeCreated: 1540944864
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,37 @@
namespace VRCSDK2.Validation.Performance
{
public enum AvatarPerformanceCategory
{
None,
Overall,
DownloadSize,
PolyCount,
AABB,
SkinnedMeshCount,
MeshCount,
MaterialCount,
DynamicBoneComponentCount,
DynamicBoneSimulatedBoneCount,
DynamicBoneColliderCount,
DynamicBoneCollisionCheckCount,
AnimatorCount,
BoneCount,
LightCount,
ParticleSystemCount,
ParticleTotalCount,
ParticleMaxMeshPolyCount,
ParticleTrailsEnabled,
ParticleCollisionEnabled,
TrailRendererCount,
LineRendererCount,
ClothCount,
ClothMaxVertices,
PhysicsColliderCount,
PhysicsRigidbodyCount,
AudioSourceCount,
AvatarPerformanceCategoryCount
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f1ce994297384ff1bc330196df61b7ca
timeCreated: 1561267912
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b99e0ada644703542a2c3f854643609f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,109 @@
using System.Collections;
using UnityEngine;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Filters
{
public abstract class AbstractPerformanceFilter : ScriptableObject
{
public abstract IEnumerator ApplyPerformanceFilter(
GameObject avatarObject,
AvatarPerformanceStats perfStats,
PerformanceRating ratingLimit,
AvatarPerformance.IgnoreDelegate shouldIgnoreComponent,
AvatarPerformance.FilterBlockCallback onBlock
);
protected static IEnumerator RemoveComponentsOfTypeEnumerator<T>(GameObject target) where T : Component
{
if(target == null)
{
yield break;
}
foreach(T targetComponent in target.GetComponentsInChildren<T>(true))
{
if(targetComponent == null || targetComponent.gameObject == null)
{
continue;
}
#if VERBOSE_COMPONENT_REMOVAL
Debug.LogWarningFormat("Removing {0} comp from {1}", targetComponent.GetType().Name, targetComponent.gameObject.name);
#endif
yield return RemoveComponent(targetComponent);
}
}
protected static IEnumerator RemoveComponent(Component targetComponent)
{
yield return RemoveDependencies(targetComponent);
Destroy(targetComponent);
yield return null;
}
protected static IEnumerator RemoveDependencies(Component targetComponent)
{
if(targetComponent == null)
{
yield break;
}
Component[] siblingComponents = targetComponent.GetComponents<Component>();
if(siblingComponents == null || siblingComponents.Length == 0)
{
yield break;
}
System.Type componentType = targetComponent.GetType();
foreach(Component siblingComponent in siblingComponents)
{
if(siblingComponent == null)
{
continue;
}
bool deleteMe = false;
object[] requireComponentAttributes = siblingComponent.GetType().GetCustomAttributes(typeof(RequireComponent), true);
if(requireComponentAttributes.Length == 0)
{
continue;
}
foreach(var requireComponentObject in requireComponentAttributes)
{
RequireComponent requireComponentAttribute = requireComponentObject as RequireComponent;
if(requireComponentAttribute == null)
{
continue;
}
if(
requireComponentAttribute.m_Type0 != componentType &&
requireComponentAttribute.m_Type1 != componentType &&
requireComponentAttribute.m_Type2 != componentType
)
{
continue;
}
deleteMe = true;
break;
}
if(!deleteMe)
{
continue;
}
#if VERBOSE_COMPONENT_REMOVAL
Debug.LogWarningFormat("Deleting component dependency {0} found on {1}", siblingComponent.GetType().Name, targetComponent.gameObject.name);
#endif
yield return RemoveComponent(siblingComponent);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: abda65e062e44213aa3e1f4c82b400a8
timeCreated: 1563937347
@@ -0,0 +1,34 @@
using System.Collections.Generic;
using UnityEngine;
namespace VRCSDK2.Validation.Performance
{
public static class MeshUtils
{
private const uint INDICES_PER_TRIANGLE = 3U;
public static uint GetMeshTriangleCount(Mesh sourceMesh)
{
if(sourceMesh == null)
{
return 0;
}
// We can't use GetIndexCount if the mesh isn't readable so just return a huge number.
// The SDK Control Panel should show a warning in this case.
if(!sourceMesh.isReadable)
{
return uint.MaxValue;
}
uint count = 0;
for(int i = 0; i < sourceMesh.subMeshCount; i++)
{
uint indexCount = sourceMesh.GetIndexCount(i);
count += indexCount / INDICES_PER_TRIANGLE;
}
return count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: f28c978154794266b38d686139c6b872
timeCreated: 1561249515
@@ -0,0 +1,46 @@
using System.Collections;
using UnityEngine;
using VRCSDK2.Validation.Performance.Filters;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New PerformanceFilterSet",
menuName = "VRC Scriptable Objects/Performance/PerformanceFilterSet"
)]
#endif
public class PerformanceFilterSet : ScriptableObject
{
public AbstractPerformanceFilter[] performanceFilters;
public IEnumerator ApplyPerformanceFilters(
GameObject avatarObject,
AvatarPerformanceStats perfStats,
PerformanceRating ratingLimit,
AvatarPerformance.IgnoreDelegate shouldIgnoreComponent,
AvatarPerformance.FilterBlockCallback onBlock
)
{
foreach(AbstractPerformanceFilter performanceFilter in performanceFilters)
{
if(performanceFilter == null)
{
continue;
}
bool avatarBlocked = false;
yield return performanceFilter.ApplyPerformanceFilter(avatarObject, perfStats, ratingLimit, shouldIgnoreComponent, () => { avatarBlocked = true; });
if(!avatarBlocked)
{
continue;
}
onBlock();
break;
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8cdca9d06d1b4732b9ccb82a38bb8d9c
timeCreated: 1563939583
@@ -0,0 +1,12 @@
namespace VRCSDK2.Validation.Performance
{
public enum PerformanceInfoDisplayLevel
{
None,
Verbose,
Info,
Warning,
Error
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a5ed7498cb1a46c78eab031f5f32448c
timeCreated: 1561267918
@@ -0,0 +1,12 @@
namespace VRCSDK2.Validation.Performance
{
public enum PerformanceRating
{
None = 0,
Excellent = 1,
Good = 2,
Medium = 3,
Poor = 4,
VeryPoor = 5
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5019a55ee9e2404c81bc61a39a010d8d
timeCreated: 1561267904
@@ -0,0 +1,44 @@
using System.Collections;
using UnityEngine;
using VRCSDK2.Validation.Performance.Scanners;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New PerformanceScannerSet",
menuName = "VRC Scriptable Objects/Performance/PerformanceScannerSet"
)]
#endif
public class PerformanceScannerSet : ScriptableObject
{
public AbstractPerformanceScanner[] performanceScanners;
public void RunPerformanceScan(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
foreach(AbstractPerformanceScanner performanceScanner in performanceScanners)
{
if(performanceScanner == null)
{
continue;
}
performanceScanner.RunPerformanceScan(avatarObject, perfStats, shouldIgnoreComponent);
}
}
public IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
foreach(AbstractPerformanceScanner performanceScanner in performanceScanners)
{
if(performanceScanner == null)
{
continue;
}
yield return performanceScanner.RunPerformanceScanEnumerator(avatarObject, perfStats, shouldIgnoreComponent);
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 4afb61f36d144fc381114cd7f78d13e7
timeCreated: 1561259704
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 67bd9cc73f0a3e64e99dac8128cdf4b2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,97 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
public abstract class AbstractPerformanceScanner : ScriptableObject
{
private const int MAXIMUM_COMPONENT_SCANS_PER_FRAME = 10;
private static int _componentScansThisFrame = 0;
private static int _componentScansFrameNumber = 0;
private readonly Stack<IEnumerator> _coroutines = new Stack<IEnumerator>();
private bool _limitComponentScansPerFrame = true;
public abstract IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent);
public void RunPerformanceScan(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
_limitComponentScansPerFrame = false;
try
{
_coroutines.Push(RunPerformanceScanEnumerator(avatarObject, perfStats, shouldIgnoreComponent));
while(_coroutines.Count > 0)
{
IEnumerator currentCoroutine = _coroutines.Peek();
if(currentCoroutine.MoveNext())
{
IEnumerator nestedCoroutine = currentCoroutine.Current as IEnumerator;
if(nestedCoroutine != null)
{
_coroutines.Push(nestedCoroutine);
}
}
else
{
_coroutines.Pop();
}
}
_coroutines.Clear();
}
finally
{
_limitComponentScansPerFrame = true;
}
}
protected IEnumerator ScanAvatarForComponentsOfType(Type componentType, GameObject avatarObject, List<Component> destinationBuffer)
{
yield return HandleComponentScansPerFrameLimit();
Profiler.BeginSample("Component Scan");
destinationBuffer.Clear();
destinationBuffer.AddRange(avatarObject.GetComponentsInChildren(componentType, true));
Profiler.EndSample();
}
protected IEnumerator ScanAvatarForComponentsOfType<T>(GameObject avatarObject, List<T> destinationBuffer)
{
yield return HandleComponentScansPerFrameLimit();
Profiler.BeginSample("Component Scan");
destinationBuffer.Clear();
avatarObject.GetComponentsInChildren(true, destinationBuffer);
Profiler.EndSample();
yield return null;
}
private IEnumerator HandleComponentScansPerFrameLimit()
{
if(!_limitComponentScansPerFrame)
{
yield break;
}
while(_componentScansThisFrame >= MAXIMUM_COMPONENT_SCANS_PER_FRAME)
{
if(Time.frameCount > _componentScansFrameNumber)
{
_componentScansFrameNumber = Time.frameCount;
_componentScansThisFrame = 0;
break;
}
yield return null;
}
_componentScansThisFrame++;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0bd0691a021844f49444a04a959d6328
timeCreated: 1561279121
@@ -0,0 +1,44 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New AnimatorPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/AnimatorPerformanceScanner"
)]
#endif
public sealed class AnimatorPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
int animatorCount = 0;
// Animators
List<Animator> animatorBuffer = new List<Animator>();
yield return ScanAvatarForComponentsOfType(avatarObject, animatorBuffer);
if(shouldIgnoreComponent != null)
{
animatorBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
// ReSharper disable once UselessBinaryOperation
animatorCount += animatorBuffer.Count;
// Animations
List<Animation> animationBuffer = new List<Animation>();
yield return ScanAvatarForComponentsOfType(avatarObject, animationBuffer);
if(shouldIgnoreComponent != null)
{
animationBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
animatorCount += animationBuffer.Count;
perfStats.animatorCount = animatorCount;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 08c8e931d0544866a0f626855d9c1841
timeCreated: 1561251363
@@ -0,0 +1,29 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New AudioPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/AudioPerformanceScanner"
)]
#endif
public sealed class AudioPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Audio Sources
List<AudioSource> audioSourceBuffer = new List<AudioSource>();
yield return ScanAvatarForComponentsOfType(avatarObject, audioSourceBuffer);
if(shouldIgnoreComponent != null)
{
audioSourceBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
perfStats.audioSourceCount = audioSourceBuffer.Count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: b3a8bba736414d1aaa9e766da27b56b5
timeCreated: 1561255618
@@ -0,0 +1,48 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New ClothPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/ClothPerformanceScanner"
)]
#endif
public sealed class ClothPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Cloth
List<Cloth> clothBuffer = new List<Cloth>();
yield return ScanAvatarForComponentsOfType(avatarObject, clothBuffer);
if(shouldIgnoreComponent != null)
{
clothBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
int totalClothVertices = 0;
foreach(Cloth cloth in clothBuffer)
{
if(cloth == null)
{
continue;
}
Vector3[] clothVertices = cloth.vertices;
if(clothVertices == null)
{
continue;
}
totalClothVertices += clothVertices.Length;
}
perfStats.clothCount = clothBuffer.Count;
perfStats.clothMaxVertices = totalClothVertices;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 0cec88b5a46f459195f10a2f11fddb2f
timeCreated: 1561255843
@@ -0,0 +1,191 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New DynamicBonePerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/DynamicBonePerformanceScanner"
)]
#endif
public sealed class DynamicBonePerformanceScanner : AbstractPerformanceScanner
{
private Type _dynamicBoneType;
private FieldInfo _dynamicBoneRootFieldInfo;
private FieldInfo _dynamicBoneExclusionsFieldInfo;
private FieldInfo _dynamicBoneCollidersFieldInfo;
private FieldInfo _dynamicBoneEndLengthFieldInfo;
private FieldInfo _dynamicBoneEndOffsetFieldInfo;
private void Awake()
{
FindDynamicBoneTypes();
}
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
if(_dynamicBoneType == null)
{
yield break;
}
// Dynamic Bone as Component
List<Component> dynamicBoneComponentBuffer = new List<Component>();
List<object> dynamicBoneColliderObjectBuffer = new List<object>();
yield return ScanAvatarForComponentsOfType(_dynamicBoneType, avatarObject, dynamicBoneComponentBuffer);
if(shouldIgnoreComponent != null)
{
dynamicBoneComponentBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
int totalSimulatedBoneCount = 0;
int totalCollisionChecks = 0;
Profiler.BeginSample("Analyze Dynamic Bones");
foreach(Component dynamicBone in dynamicBoneComponentBuffer)
{
Profiler.BeginSample("Single Dynamic Bone Component");
int simulatedBones = 0;
// Add extra bones to the end of each chain if end bones are being used.
float endLength = (float)_dynamicBoneEndLengthFieldInfo.GetValue(dynamicBone);
Vector3 endOffset = (Vector3)_dynamicBoneEndOffsetFieldInfo.GetValue(dynamicBone);
bool hasEndBones = endLength > 0 || endOffset != Vector3.zero;
Transform root = (Transform)_dynamicBoneRootFieldInfo.GetValue(dynamicBone);
if(root != null)
{
List<Transform> exclusions = (List<Transform>)_dynamicBoneExclusionsFieldInfo.GetValue(dynamicBone);
// Calculate number of simulated bones for the hierarchy
simulatedBones = CountTransformsRecursively(root, exclusions, hasEndBones);
totalSimulatedBoneCount += simulatedBones;
}
int colliderListEntryCount = 0;
IList colliderList = (IList)_dynamicBoneCollidersFieldInfo.GetValue(dynamicBone);
if(colliderList != null)
{
foreach(object collider in colliderList)
{
colliderListEntryCount += 1;
if(collider != null && !dynamicBoneColliderObjectBuffer.Contains(collider))
{
dynamicBoneColliderObjectBuffer.Add(collider);
}
}
}
// The root bone is skipped in collision checks.
totalCollisionChecks += (simulatedBones - 1) * colliderListEntryCount;
Profiler.EndSample();
}
Profiler.EndSample();
yield return null;
perfStats.dynamicBoneComponentCount = dynamicBoneComponentBuffer.Count;
perfStats.dynamicBoneSimulatedBoneCount = totalSimulatedBoneCount;
perfStats.dynamicBoneColliderCount = dynamicBoneColliderObjectBuffer.Count;
perfStats.dynamicBoneCollisionCheckCount = totalCollisionChecks;
}
private void FindDynamicBoneTypes()
{
if(_dynamicBoneType != null)
{
return;
}
Type dyBoneType = ValidationUtils.GetTypeFromName("DynamicBone");
if(dyBoneType == null)
{
return;
}
Type dyBoneColliderType = ValidationUtils.GetTypeFromName("DynamicBoneColliderBase") ?? ValidationUtils.GetTypeFromName("DynamicBoneCollider");
if(dyBoneColliderType == null)
{
return;
}
FieldInfo rootFieldInfo = dyBoneType.GetField("m_Root", BindingFlags.Public | BindingFlags.Instance);
if(rootFieldInfo == null || rootFieldInfo.FieldType != typeof(Transform))
{
return;
}
FieldInfo exclusionsFieldInfo = dyBoneType.GetField("m_Exclusions", BindingFlags.Public | BindingFlags.Instance);
if(exclusionsFieldInfo == null || exclusionsFieldInfo.FieldType != typeof(List<Transform>))
{
return;
}
FieldInfo collidersFieldInfo = dyBoneType.GetField("m_Colliders", BindingFlags.Public | BindingFlags.Instance);
if(collidersFieldInfo == null || collidersFieldInfo.FieldType.GetGenericTypeDefinition() != typeof(List<>) ||
collidersFieldInfo.FieldType.GetGenericArguments().Single() != dyBoneColliderType)
{
return;
}
FieldInfo endLengthFieldInfo = dyBoneType.GetField("m_EndLength", BindingFlags.Public | BindingFlags.Instance);
if(endLengthFieldInfo == null || endLengthFieldInfo.FieldType != typeof(float))
{
return;
}
FieldInfo endOffsetFieldInfo = dyBoneType.GetField("m_EndOffset", BindingFlags.Public | BindingFlags.Instance);
if(endOffsetFieldInfo == null || endOffsetFieldInfo.FieldType != typeof(Vector3))
{
return;
}
_dynamicBoneType = dyBoneType;
_dynamicBoneRootFieldInfo = rootFieldInfo;
_dynamicBoneExclusionsFieldInfo = exclusionsFieldInfo;
_dynamicBoneCollidersFieldInfo = collidersFieldInfo;
_dynamicBoneEndLengthFieldInfo = endLengthFieldInfo;
_dynamicBoneEndOffsetFieldInfo = endOffsetFieldInfo;
}
// Like DynamicBone itself exclusions only apply to children of the current bone.
// This means the root bone itself never excluded.
private static int CountTransformsRecursively(Transform transform, List<Transform> exclusions, bool addEndBones)
{
if(transform == null)
{
return 0;
}
int count = 1;
int childCount = transform.childCount;
if(childCount > 0)
{
foreach(Transform child in transform)
{
if(exclusions == null || !exclusions.Contains(child))
{
count += CountTransformsRecursively(child, exclusions, addEndBones);
}
}
}
else
{
if(addEndBones)
{
count++;
}
}
return count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a226df494ef04404a9a47c714822ab9f
timeCreated: 1561251560
@@ -0,0 +1,30 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New LightPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/LightPerformanceScanner"
)]
#endif
public sealed class LightPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Lights
List<Light> lightBuffer = new List<Light>();
yield return ScanAvatarForComponentsOfType(avatarObject, lightBuffer);
if(shouldIgnoreComponent != null)
{
lightBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
perfStats.lightCount = lightBuffer.Count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 405778fdc32c44c1bb9fdd0476fb0007
timeCreated: 1561256390
@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New LineRendererPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/LineRendererPerformanceScanner"
)]
#endif
public sealed class LineRendererPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Line Renderers
List<LineRenderer> lineRendererBuffer = new List<LineRenderer>();
yield return ScanAvatarForComponentsOfType(avatarObject, lineRendererBuffer);
if(shouldIgnoreComponent != null)
{
lineRendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
int numLineRenderers = lineRendererBuffer.Count;
perfStats.lineRendererCount = numLineRenderers;
perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + numLineRenderers;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ec87392b85844f7bb526a48ec866a8f0
timeCreated: 1561253047
@@ -0,0 +1,266 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New MeshPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/MeshPerformanceScanner"
)]
#endif
public sealed class MeshPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Renderers
List<Renderer> rendererBuffer = new List<Renderer>(16);
yield return ScanAvatarForComponentsOfType(avatarObject, rendererBuffer);
if(shouldIgnoreComponent != null)
{
rendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
yield return AnalyzeGeometry(avatarObject, rendererBuffer, perfStats);
AnalyzeMeshRenderers(rendererBuffer, perfStats);
AnalyzeSkinnedMeshRenderers(rendererBuffer, perfStats);
yield return null;
}
private static uint CalculateRendererPolyCount(Renderer renderer)
{
Mesh sharedMesh = null;
SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
if(skinnedMeshRenderer != null)
{
sharedMesh = skinnedMeshRenderer.sharedMesh;
}
if(sharedMesh == null)
{
MeshRenderer meshRenderer = renderer as MeshRenderer;
if(meshRenderer != null)
{
MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
if(meshFilter != null)
{
sharedMesh = meshFilter.sharedMesh;
}
}
}
if(sharedMesh == null)
{
return 0;
}
return MeshUtils.GetMeshTriangleCount(sharedMesh);
}
private static bool RendererHasMesh(Renderer renderer)
{
MeshRenderer meshRenderer = renderer as MeshRenderer;
if(meshRenderer != null)
{
MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
if(meshFilter == null)
{
return false;
}
return meshFilter.sharedMesh != null;
}
SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
if(skinnedMeshRenderer != null)
{
return skinnedMeshRenderer.sharedMesh != null;
}
return false;
}
private IEnumerator AnalyzeGeometry(GameObject avatarObject, IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
{
List<Renderer> lodGroupRendererIgnoreBuffer = new List<Renderer>(16);
List<LODGroup> lodBuffer = new List<LODGroup>(16);
ulong polyCount = 0;
Bounds bounds = new Bounds(avatarObject.transform.position, Vector3.zero);
yield return ScanAvatarForComponentsOfType(avatarObject, lodBuffer);
try
{
foreach(LODGroup lodGroup in lodBuffer)
{
LOD[] lodLevels = lodGroup.GetLODs();
ulong highestLodPolyCount = 0;
foreach(LOD lod in lodLevels)
{
uint thisLodPolyCount = 0;
foreach(Renderer renderer in lod.renderers)
{
lodGroupRendererIgnoreBuffer.Add(renderer);
checked
{
thisLodPolyCount += CalculateRendererPolyCount(renderer);
}
}
if(thisLodPolyCount > highestLodPolyCount)
{
highestLodPolyCount = thisLodPolyCount;
}
}
checked
{
polyCount += highestLodPolyCount;
}
}
}
catch(OverflowException e)
{
VRC.Core.Logger.Log("Overflow exception while analyzing geometry, assuming max value:" + e.ToString(), VRC.Core.DebugLevel.All);
polyCount = uint.MaxValue;
}
Profiler.BeginSample("Calculate Total Polygon Count and Bounds");
foreach(Renderer renderer in renderers)
{
Profiler.BeginSample("Single Renderer");
if(renderer is MeshRenderer || renderer is SkinnedMeshRenderer)
{
if(!RendererHasMesh(renderer))
{
Profiler.EndSample();
continue;
}
bounds.Encapsulate(renderer.bounds);
}
if(lodGroupRendererIgnoreBuffer.Contains(renderer))
{
Profiler.EndSample();
continue;
}
polyCount += CalculateRendererPolyCount(renderer);
Profiler.EndSample();
}
Profiler.EndSample();
bounds.center -= avatarObject.transform.position;
lodGroupRendererIgnoreBuffer.Clear();
lodBuffer.Clear();
perfStats.polyCount = polyCount > int.MaxValue ? int.MaxValue : (int)polyCount;
perfStats.aabb = bounds;
}
private static void AnalyzeSkinnedMeshRenderers(IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
{
Profiler.BeginSample("AnalyzeSkinnedMeshRenderers");
int count = 0;
int materialSlots = 0;
int skinnedBoneCount = 0;
HashSet<Transform> transformIgnoreBuffer = new HashSet<Transform>();
foreach(Renderer renderer in renderers)
{
Profiler.BeginSample("Analyze SkinnedMeshRenderer");
SkinnedMeshRenderer skinnedMeshRenderer = renderer as SkinnedMeshRenderer;
if(skinnedMeshRenderer == null)
{
Profiler.EndSample();
continue;
}
count++;
Mesh sharedMesh = skinnedMeshRenderer.sharedMesh;
if(sharedMesh != null)
{
materialSlots += sharedMesh.subMeshCount;
}
// bone count
Profiler.BeginSample("Count Bones");
Transform[] bones = skinnedMeshRenderer.bones;
foreach(Transform bone in bones)
{
Profiler.BeginSample("Count Bone");
if(bone == null || transformIgnoreBuffer.Contains(bone))
{
Profiler.EndSample();
continue;
}
transformIgnoreBuffer.Add(bone);
skinnedBoneCount++;
Profiler.EndSample();
}
Profiler.EndSample();
Profiler.EndSample();
}
transformIgnoreBuffer.Clear();
Profiler.EndSample();
perfStats.skinnedMeshCount = count;
perfStats.boneCount = skinnedBoneCount;
perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
}
private static void AnalyzeMeshRenderers(IEnumerable<Renderer> renderers, AvatarPerformanceStats perfStats)
{
Profiler.BeginSample("AnalyzeMeshRenderers");
int count = 0;
int materialSlots = 0;
foreach(Renderer renderer in renderers)
{
Profiler.BeginSample("Analyze MeshRenderer");
MeshRenderer meshRenderer = renderer as MeshRenderer;
if(meshRenderer == null)
{
Profiler.EndSample();
continue;
}
count++;
Profiler.BeginSample("Get MeshFilter");
MeshFilter meshFilter = meshRenderer.GetComponent<MeshFilter>();
Profiler.EndSample();
if(meshFilter == null)
{
Profiler.EndSample();
continue;
}
Mesh sharedMesh = meshFilter.sharedMesh;
if(sharedMesh != null)
{
materialSlots += sharedMesh.subMeshCount;
}
}
Profiler.EndSample();
perfStats.meshCount = count;
perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 38bca10261df4ddfa10cff3b3bbb9428
timeCreated: 1561249198
@@ -0,0 +1,114 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New ParticlePerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/ParticlePerformanceScanner"
)]
#endif
public sealed class ParticlePerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Particle Systems
List<ParticleSystem> particleSystemBuffer = new List<ParticleSystem>();
yield return ScanAvatarForComponentsOfType(avatarObject, particleSystemBuffer);
if(shouldIgnoreComponent != null)
{
particleSystemBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
AnalyzeParticleSystemRenderers(particleSystemBuffer, perfStats);
yield return null;
}
private static void AnalyzeParticleSystemRenderers(IEnumerable<ParticleSystem> particleSystems, AvatarPerformanceStats perfStats)
{
int particleSystemCount = 0;
ulong particleTotalCount = 0;
ulong particleTotalMaxMeshPolyCount = 0;
bool particleTrailsEnabled = false;
bool particleCollisionEnabled = false;
int materialSlots = 0;
Profiler.BeginSample("AnalyzeParticleSystemRenderers");
foreach(ParticleSystem particleSystem in particleSystems)
{
Profiler.BeginSample("Single Particle System");
int particleCount = particleSystem.main.maxParticles;
if(particleCount <= 0)
{
Profiler.EndSample();
continue;
}
particleSystemCount++;
particleTotalCount += (uint)particleCount;
ParticleSystemRenderer particleSystemRenderer = particleSystem.GetComponent<ParticleSystemRenderer>();
if(particleSystemRenderer == null)
{
Profiler.EndSample();
continue;
}
materialSlots++;
// mesh particles
if(particleSystemRenderer.renderMode == ParticleSystemRenderMode.Mesh && particleSystemRenderer.meshCount > 0)
{
uint highestPolyCount = 0;
Mesh[] meshes = new Mesh[particleSystemRenderer.meshCount];
int particleRendererMeshCount = particleSystemRenderer.GetMeshes(meshes);
for(int meshIndex = 0; meshIndex < particleRendererMeshCount; meshIndex++)
{
Mesh mesh = meshes[meshIndex];
if(mesh == null)
{
continue;
}
uint polyCount = MeshUtils.GetMeshTriangleCount(mesh);
if(polyCount > highestPolyCount)
{
highestPolyCount = polyCount;
}
}
ulong maxMeshParticlePolyCount = (uint)particleCount * highestPolyCount;
particleTotalMaxMeshPolyCount += maxMeshParticlePolyCount;
}
if(particleSystem.trails.enabled)
{
particleTrailsEnabled = true;
materialSlots++;
}
if(particleSystem.collision.enabled)
{
particleCollisionEnabled = true;
}
Profiler.EndSample();
}
Profiler.EndSample();
perfStats.particleSystemCount = particleSystemCount;
perfStats.particleTotalCount = particleTotalCount > int.MaxValue ? int.MaxValue : (int)particleTotalCount;
perfStats.particleMaxMeshPolyCount = particleTotalMaxMeshPolyCount > int.MaxValue ? int.MaxValue : (int)particleTotalMaxMeshPolyCount;
perfStats.particleTrailsEnabled = particleTrailsEnabled;
perfStats.particleCollisionEnabled = particleCollisionEnabled;
perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + materialSlots;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 10723e354ff14f98a49ab797b3f005e6
timeCreated: 1561250267
@@ -0,0 +1,64 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New PhysicsPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/PhysicsPerformanceScanner"
)]
#endif
public sealed class PhysicsPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Colliders
List<Collider> colliderBuffer = new List<Collider>();
yield return ScanAvatarForComponentsOfType(avatarObject, colliderBuffer);
colliderBuffer.RemoveAll(
o =>
{
if(shouldIgnoreComponent != null && shouldIgnoreComponent(o))
{
return true;
}
if(o.GetComponent<VRC.SDKBase.VRCStation>() != null)
{
return true;
}
return false;
}
);
perfStats.physicsColliderCount = colliderBuffer.Count;
// Rigidbodies
List<Rigidbody> rigidbodyBuffer = new List<Rigidbody>();
yield return ScanAvatarForComponentsOfType(avatarObject, rigidbodyBuffer);
rigidbodyBuffer.RemoveAll(
o =>
{
if(shouldIgnoreComponent != null && shouldIgnoreComponent(o))
{
return true;
}
if(o.GetComponent<VRC.SDKBase.VRCStation>() != null)
{
return true;
}
return false;
}
);
perfStats.physicsRigidbodyCount = rigidbodyBuffer.Count;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6a94ecdeecd04f85824cc3244be5712a
timeCreated: 1561256481
@@ -0,0 +1,32 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Profiling;
using VRCSDK2.Validation.Performance.Stats;
namespace VRCSDK2.Validation.Performance.Scanners
{
#if VRC_CLIENT
[CreateAssetMenu(
fileName = "New TrailRendererPerformanceScanner",
menuName = "VRC Scriptable Objects/Performance/Avatar/Scanners/TrailRendererPerformanceScanner"
)]
#endif
public sealed class TrailRendererPerformanceScanner : AbstractPerformanceScanner
{
public override IEnumerator RunPerformanceScanEnumerator(GameObject avatarObject, AvatarPerformanceStats perfStats, AvatarPerformance.IgnoreDelegate shouldIgnoreComponent)
{
// Trail Renderers
List<TrailRenderer> trailRendererBuffer = new List<TrailRenderer>();
yield return ScanAvatarForComponentsOfType(avatarObject, trailRendererBuffer);
if(shouldIgnoreComponent != null)
{
trailRendererBuffer.RemoveAll(c => shouldIgnoreComponent(c));
}
int numTrailRenderers = trailRendererBuffer.Count;
perfStats.trailRendererCount = numTrailRenderers;
perfStats.materialCount = perfStats.materialCount.GetValueOrDefault() + numTrailRenderers;
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 2efd714b564547b4be1ebd1f2700668b
timeCreated: 1561251810
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 68702a268e708b043a0dca66980158fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,589 @@
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace VRCSDK2.Validation.Performance.Stats
{
public class AvatarPerformanceStats
{
private delegate int ComparePerformanceStatsDelegate(AvatarPerformanceStats stats, AvatarPerformanceStatsLevel statsLevel);
#region Public Fields
public string avatarName;
public int? polyCount;
public Bounds? aabb;
public int? skinnedMeshCount;
public int? meshCount;
public int? materialCount;
public int? animatorCount;
public int? boneCount;
public int? lightCount;
public int? particleSystemCount;
public int? particleTotalCount;
public int? particleMaxMeshPolyCount;
public bool? particleTrailsEnabled;
public bool? particleCollisionEnabled;
public int? trailRendererCount;
public int? lineRendererCount;
public int? dynamicBoneComponentCount;
public int? dynamicBoneSimulatedBoneCount;
public int? dynamicBoneColliderCount;
public int? dynamicBoneCollisionCheckCount; // number of collider simulated bones excluding the root multiplied by the number of colliders
public int? clothCount;
public int? clothMaxVertices;
public int? physicsColliderCount;
public int? physicsRigidbodyCount;
public int? audioSourceCount;
public float? downloadSize;
#endregion
#region Private Fields
private readonly PerformanceRating[] _performanceRatingCache;
private static readonly Dictionary<AvatarPerformanceCategory, string> _performanceCategoryDisplayNames = new Dictionary<AvatarPerformanceCategory, string>
{
{AvatarPerformanceCategory.PolyCount, "Polygons"},
{AvatarPerformanceCategory.AABB, "Bounds"},
{AvatarPerformanceCategory.SkinnedMeshCount, "Skinned Meshes"},
{AvatarPerformanceCategory.MeshCount, "Meshes"},
{AvatarPerformanceCategory.MaterialCount, "Material Slots"},
{AvatarPerformanceCategory.AnimatorCount, "Animators"},
{AvatarPerformanceCategory.BoneCount, "Bones"},
{AvatarPerformanceCategory.LightCount, "Lights"},
{AvatarPerformanceCategory.ParticleSystemCount, "Particle Systems"},
{AvatarPerformanceCategory.ParticleTotalCount, "Total Max Particles"},
{AvatarPerformanceCategory.ParticleMaxMeshPolyCount, "Mesh Particle Max Polygons"},
{AvatarPerformanceCategory.ParticleTrailsEnabled, "Particle Trails Enabled"},
{AvatarPerformanceCategory.ParticleCollisionEnabled, "Particle Collision Enabled"},
{AvatarPerformanceCategory.TrailRendererCount, "Trail Renderers"},
{AvatarPerformanceCategory.LineRendererCount, "Line Renderers"},
{AvatarPerformanceCategory.DynamicBoneComponentCount, "Dynamic Bone Components"},
{AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount, "Dynamic Bone Transforms"},
{AvatarPerformanceCategory.DynamicBoneColliderCount, "Dynamic Bone Colliders"},
{AvatarPerformanceCategory.DynamicBoneCollisionCheckCount, "Dynamic Bone Collision Check Count"},
{AvatarPerformanceCategory.ClothCount, "Cloths"},
{AvatarPerformanceCategory.ClothMaxVertices, "Total Cloth Vertices"},
{AvatarPerformanceCategory.PhysicsColliderCount, "Physics Colliders"},
{AvatarPerformanceCategory.PhysicsRigidbodyCount, "Physics Rigidbodies"},
{AvatarPerformanceCategory.AudioSourceCount, "Audio Sources"},
{AvatarPerformanceCategory.DownloadSize, "Download Size"},
};
private static readonly Dictionary<PerformanceRating, string> _performanceRatingDisplayNames = new Dictionary<PerformanceRating, string>
{
{PerformanceRating.None, "None"},
{PerformanceRating.Excellent, "Excellent"},
{PerformanceRating.Good, "Good"},
{PerformanceRating.Medium, "Medium"},
{PerformanceRating.Poor, "Poor"},
{PerformanceRating.VeryPoor, "VeryPoor"}
};
#endregion
#region Initialization
private static AvatarPerformanceStatsLevelSet _performanceStatsLevelSet = null;
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
public static void Initialize()
{
if(_performanceStatsLevelSet != null)
{
return;
}
_performanceStatsLevelSet = Resources.Load<AvatarPerformanceStatsLevelSet>(GetPlatformPerformanceStatLevels());
}
private static string GetPlatformPerformanceStatLevels()
{
#if UNITY_ANDROID
return "Validation/Performance/StatsLevels/Quest/AvatarPerformanceStatLevels_Quest";
#else
return "Validation/Performance/StatsLevels/Windows/AvatarPerformanceStatLevels_Windows";
#endif
}
#endregion
#region Constructors
public AvatarPerformanceStats()
{
_performanceRatingCache = new PerformanceRating[(int)AvatarPerformanceCategory.AvatarPerformanceCategoryCount];
}
#endregion
#region Public Methods
public void Reset()
{
avatarName = null;
polyCount = null;
aabb = null;
skinnedMeshCount = null;
meshCount = null;
materialCount = null;
animatorCount = null;
boneCount = null;
lightCount = null;
particleSystemCount = null;
particleTotalCount = null;
particleMaxMeshPolyCount = null;
particleTrailsEnabled = null;
particleCollisionEnabled = null;
trailRendererCount = null;
lineRendererCount = null;
dynamicBoneComponentCount = null;
dynamicBoneSimulatedBoneCount = null;
dynamicBoneColliderCount = null;
dynamicBoneCollisionCheckCount = null;
clothCount = null;
clothMaxVertices = null;
physicsColliderCount = null;
physicsRigidbodyCount = null;
audioSourceCount = null;
downloadSize = null;
for(int i = 0; i < (int)AvatarPerformanceCategory.AvatarPerformanceCategoryCount; i++)
{
_performanceRatingCache[i] = PerformanceRating.None;
}
}
public PerformanceRating GetPerformanceRatingForCategory(AvatarPerformanceCategory perfCategory)
{
if(_performanceRatingCache[(int)perfCategory] == PerformanceRating.None)
{
_performanceRatingCache[(int)perfCategory] = CalculatePerformanceRatingForCategory(perfCategory);
}
return _performanceRatingCache[(int)perfCategory];
}
public void CalculateAllPerformanceRatings()
{
for(int i = 0; i < _performanceRatingCache.Length; i++)
{
_performanceRatingCache[i] = PerformanceRating.None;
}
foreach(AvatarPerformanceCategory perfCategory in Enum.GetValues(typeof(AvatarPerformanceCategory)))
{
if(perfCategory == AvatarPerformanceCategory.None ||
perfCategory == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
{
continue;
}
if(_performanceRatingCache[(int)perfCategory] == PerformanceRating.None)
{
_performanceRatingCache[(int)perfCategory] = CalculatePerformanceRatingForCategory(perfCategory);
}
}
}
public static string GetPerformanceCategoryDisplayName(AvatarPerformanceCategory category)
{
return _performanceCategoryDisplayNames[category];
}
public static string GetPerformanceRatingDisplayName(PerformanceRating rating)
{
return _performanceRatingDisplayNames[rating];
}
public static AvatarPerformanceStatsLevel GetStatLevelForRating(PerformanceRating rating)
{
switch(rating)
{
case PerformanceRating.None:
return _performanceStatsLevelSet.excellent;
case PerformanceRating.Excellent:
return _performanceStatsLevelSet.excellent;
case PerformanceRating.Good:
return _performanceStatsLevelSet.good;
case PerformanceRating.Medium:
return _performanceStatsLevelSet.medium;
case PerformanceRating.Poor:
return _performanceStatsLevelSet.poor;
case PerformanceRating.VeryPoor:
return _performanceStatsLevelSet.poor;
default:
return _performanceStatsLevelSet.excellent;
}
}
#endregion
#region Private Methods
private PerformanceRating CalculatePerformanceRatingForCategory(AvatarPerformanceCategory perfCategory)
{
switch(perfCategory)
{
case AvatarPerformanceCategory.Overall:
{
PerformanceRating maxRating = PerformanceRating.None;
foreach(AvatarPerformanceCategory category in Enum.GetValues(typeof(AvatarPerformanceCategory)))
{
if(category == AvatarPerformanceCategory.None ||
category == AvatarPerformanceCategory.Overall ||
category == AvatarPerformanceCategory.AvatarPerformanceCategoryCount)
{
continue;
}
PerformanceRating rating = GetPerformanceRatingForCategory(category);
if(rating > maxRating)
{
maxRating = rating;
}
}
return maxRating;
}
case AvatarPerformanceCategory.PolyCount:
{
if(!polyCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.polyCount.GetValueOrDefault() - y.polyCount);
}
case AvatarPerformanceCategory.AABB:
{
if(!aabb.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating(
(x, y) =>
ApproxLessOrEqual(y.aabb.extents.x, 0.0f) || // -1 extents means "no AABB limit"
(
ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.x, y.aabb.extents.x) &&
ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.y, y.aabb.extents.y) &&
ApproxLessOrEqual(x.aabb.GetValueOrDefault().extents.z, y.aabb.extents.z))
? -1
: 1
);
}
case AvatarPerformanceCategory.SkinnedMeshCount:
{
if(!skinnedMeshCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.skinnedMeshCount.GetValueOrDefault() - y.skinnedMeshCount);
}
case AvatarPerformanceCategory.MeshCount:
{
if(!meshCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.meshCount.GetValueOrDefault() - y.meshCount);
}
case AvatarPerformanceCategory.MaterialCount:
{
if(!materialCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.materialCount.GetValueOrDefault() - y.materialCount);
}
case AvatarPerformanceCategory.AnimatorCount:
{
if(!animatorCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.animatorCount.GetValueOrDefault() - y.animatorCount);
}
case AvatarPerformanceCategory.BoneCount:
{
if(!boneCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.boneCount.GetValueOrDefault() - y.boneCount);
}
case AvatarPerformanceCategory.LightCount:
{
if(!lightCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.lightCount.GetValueOrDefault() - y.lightCount);
}
case AvatarPerformanceCategory.ParticleSystemCount:
{
if(!particleSystemCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.particleSystemCount.GetValueOrDefault() - y.particleSystemCount);
}
case AvatarPerformanceCategory.ParticleTotalCount:
{
if(!particleTotalCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.particleTotalCount.GetValueOrDefault() - y.particleTotalCount);
}
case AvatarPerformanceCategory.ParticleMaxMeshPolyCount:
{
if(!particleMaxMeshPolyCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.particleMaxMeshPolyCount.GetValueOrDefault() - y.particleMaxMeshPolyCount);
}
case AvatarPerformanceCategory.ParticleTrailsEnabled:
{
if(!particleTrailsEnabled.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating(
(x, y) =>
{
if(x.particleTrailsEnabled == y.particleTrailsEnabled)
{
return 0;
}
return x.particleTrailsEnabled.GetValueOrDefault() ? 1 : -1;
});
}
case AvatarPerformanceCategory.ParticleCollisionEnabled:
{
if(!particleCollisionEnabled.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating(
(x, y) =>
{
if(x.particleCollisionEnabled == y.particleCollisionEnabled)
{
return 0;
}
return x.particleCollisionEnabled.GetValueOrDefault() ? 1 : -1;
});
}
case AvatarPerformanceCategory.TrailRendererCount:
{
if(!trailRendererCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.trailRendererCount.GetValueOrDefault() - y.trailRendererCount);
}
case AvatarPerformanceCategory.LineRendererCount:
{
if(!lineRendererCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.lineRendererCount.GetValueOrDefault() - y.lineRendererCount);
}
case AvatarPerformanceCategory.DynamicBoneComponentCount:
{
if(!dynamicBoneComponentCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.dynamicBoneComponentCount.GetValueOrDefault() - y.dynamicBoneComponentCount);
}
case AvatarPerformanceCategory.DynamicBoneSimulatedBoneCount:
{
if(!dynamicBoneSimulatedBoneCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.dynamicBoneSimulatedBoneCount.GetValueOrDefault() - y.dynamicBoneSimulatedBoneCount);
}
case AvatarPerformanceCategory.DynamicBoneColliderCount:
{
if(!dynamicBoneColliderCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.dynamicBoneColliderCount.GetValueOrDefault() - y.dynamicBoneColliderCount);
}
case AvatarPerformanceCategory.DynamicBoneCollisionCheckCount:
{
if(!dynamicBoneCollisionCheckCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.dynamicBoneCollisionCheckCount.GetValueOrDefault() - y.dynamicBoneCollisionCheckCount);
}
case AvatarPerformanceCategory.ClothCount:
{
if(!clothCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.clothCount.GetValueOrDefault() - y.clothCount);
}
case AvatarPerformanceCategory.ClothMaxVertices:
{
if(!clothMaxVertices.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.clothMaxVertices.GetValueOrDefault() - y.clothMaxVertices);
}
case AvatarPerformanceCategory.PhysicsColliderCount:
{
if(!physicsColliderCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.physicsColliderCount.GetValueOrDefault() - y.physicsColliderCount);
}
case AvatarPerformanceCategory.PhysicsRigidbodyCount:
{
if(!physicsRigidbodyCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.physicsRigidbodyCount.GetValueOrDefault() - y.physicsRigidbodyCount);
}
case AvatarPerformanceCategory.AudioSourceCount:
{
if(!audioSourceCount.HasValue)
{
return PerformanceRating.None;
}
return CalculatePerformanceRating((x, y) => x.audioSourceCount.GetValueOrDefault() - y.audioSourceCount);
}
case AvatarPerformanceCategory.DownloadSize:
{
if(!downloadSize.HasValue)
{
return PerformanceRating.None;
}
return PerformanceRating.Excellent;
}
default:
{
return PerformanceRating.None;
}
}
}
private PerformanceRating CalculatePerformanceRating(ComparePerformanceStatsDelegate compareFn)
{
if(compareFn(this, _performanceStatsLevelSet.excellent) <= 0)
{
return PerformanceRating.Excellent;
}
if(compareFn(this, _performanceStatsLevelSet.good) <= 0)
{
return PerformanceRating.Good;
}
if(compareFn(this, _performanceStatsLevelSet.medium) <= 0)
{
return PerformanceRating.Medium;
}
if(compareFn(this, _performanceStatsLevelSet.poor) <= 0)
{
return PerformanceRating.Poor;
}
return PerformanceRating.VeryPoor;
}
private static bool ApproxLessOrEqual(float x1, float x2)
{
float r = x1 - x2;
return r < 0.0f || Mathf.Approximately(r, 0.0f);
}
#endregion
#region Overrides
public override string ToString()
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.AppendFormat("Avatar Name: {0}\n", avatarName);
sb.AppendFormat("Overall Performance: {0}\n", GetPerformanceRatingForCategory(AvatarPerformanceCategory.Overall));
sb.AppendFormat("Poly Count: {0}\n", polyCount);
sb.AppendFormat("Bounds: {0}\n", aabb.ToString());
sb.AppendFormat("Skinned Mesh Count: {0}\n", skinnedMeshCount);
sb.AppendFormat("Mesh Count: {0}\n", meshCount);
sb.AppendFormat("Material Count: {0}\n", materialCount);
sb.AppendFormat("Animator Count: {0}\n", animatorCount);
sb.AppendFormat("Bone Count: {0}\n", boneCount);
sb.AppendFormat("Light Count: {0}\n", lightCount);
sb.AppendFormat("Particle System Count: {0}\n", particleSystemCount);
sb.AppendFormat("Particle Total Count: {0}\n", particleTotalCount);
sb.AppendFormat("Particle Max Mesh Poly Count: {0}\n", particleMaxMeshPolyCount);
sb.AppendFormat("Particle Trails Enabled: {0}\n", particleTrailsEnabled);
sb.AppendFormat("Particle Collision Enabled: {0}\n", particleCollisionEnabled);
sb.AppendFormat("Trail Renderer Count: {0}\n", trailRendererCount);
sb.AppendFormat("Line Renderer Count: {0}\n", lineRendererCount);
sb.AppendFormat("Dynamic Bone Component Count: {0}\n", dynamicBoneComponentCount);
sb.AppendFormat("Dynamic Bone Simulated Bone Count: {0}\n", dynamicBoneSimulatedBoneCount);
sb.AppendFormat("Dynamic Bone Collider Count: {0}\n", dynamicBoneColliderCount);
sb.AppendFormat("Dynamic Bone Collision Check Count: {0}\n", dynamicBoneCollisionCheckCount);
sb.AppendFormat("Cloth Count: {0}\n", clothCount);
sb.AppendFormat("Cloth Max Vertices: {0}\n", clothMaxVertices);
sb.AppendFormat("Physics Collider Count: {0}\n", physicsColliderCount);
sb.AppendFormat("Physics Rigidbody Count: {0}\n", physicsRigidbodyCount);
if(downloadSize > 0)
{
sb.AppendFormat("Download Size: {0} MB\n", downloadSize);
}
return sb.ToString();
}
#endregion
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1bf4fb79a49d4b109c4dce6b38e5548e
timeCreated: 1561267926
@@ -0,0 +1,32 @@
using UnityEngine;
namespace VRCSDK2.Validation.Performance.Stats
{
public class AvatarPerformanceStatsLevel : ScriptableObject
{
public int polyCount;
public Bounds aabb;
public int skinnedMeshCount;
public int meshCount;
public int materialCount;
public int animatorCount;
public int boneCount;
public int lightCount;
public int particleSystemCount;
public int particleTotalCount;
public int particleMaxMeshPolyCount;
public bool particleTrailsEnabled;
public bool particleCollisionEnabled;
public int trailRendererCount;
public int lineRendererCount;
public int dynamicBoneComponentCount;
public int dynamicBoneSimulatedBoneCount;
public int dynamicBoneColliderCount;
public int dynamicBoneCollisionCheckCount;
public int clothCount;
public int clothMaxVertices;
public int physicsColliderCount;
public int physicsRigidbodyCount;
public int audioSourceCount;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f742c36dce5730f4d96e37d82c330584
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,15 @@
using UnityEngine;
using UnityEngine.Serialization;
namespace VRCSDK2.Validation.Performance.Stats
{
public class AvatarPerformanceStatsLevelSet : ScriptableObject
{
[FormerlySerializedAs("veryGood")]
public AvatarPerformanceStatsLevel excellent;
public AvatarPerformanceStatsLevel good;
public AvatarPerformanceStatsLevel medium;
[FormerlySerializedAs("bad")]
public AvatarPerformanceStatsLevel poor;
}
}
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 468554b1bfc447f41a20a2f5bae65d16
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,86 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace VRCSDK2.Validation
{
public static class ShaderValidation
{
public static IEnumerable<Shader> FindIllegalShaders(GameObject target, string[] whitelist)
{
List<Shader> illegalShaders = new List<Shader>();
IEnumerator seeker = FindIllegalShadersEnumerator(target, whitelist, (c) => illegalShaders.Add(c));
while(seeker.MoveNext())
{
}
return illegalShaders;
}
private static IEnumerator FindIllegalShadersEnumerator(GameObject target, string[] whitelist, System.Action<Shader> onFound, bool useWatch = false)
{
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
if(useWatch)
{
watch.Start();
}
List<Material> materialCache = new List<Material>();
Queue<GameObject> children = new Queue<GameObject>();
children.Enqueue(target.gameObject);
while(children.Count > 0)
{
GameObject child = children.Dequeue();
if(child == null)
{
continue;
}
for(int idx = 0; idx < child.transform.childCount; ++idx)
{
children.Enqueue(child.transform.GetChild(idx).gameObject);
}
foreach(Renderer childRenderers in child.transform.GetComponents<Renderer>())
{
if(childRenderers == null)
{
continue;
}
foreach(Material sharedMaterial in childRenderers.sharedMaterials)
{
if(materialCache.Any(cacheMtl => sharedMaterial == cacheMtl)) // did we already look at this one?
{
continue;
}
// Skip empty material slots, or materials without shaders.
// Both will end up using the magenta error shader.
if(sharedMaterial == null || sharedMaterial.shader == null)
{
continue;
}
if(whitelist.All(okayShaderName => sharedMaterial.shader.name != okayShaderName))
{
onFound(sharedMaterial.shader);
yield return null;
}
materialCache.Add(sharedMaterial);
}
if(!useWatch || watch.ElapsedMilliseconds <= 1)
{
continue;
}
yield return null;
watch.Reset();
}
}
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: bef0a8d1d2c547119a62b7d7a5c512ea
timeCreated: 1563940360
@@ -0,0 +1,243 @@
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);
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 8a90ec11b51863c4cb2d8a8cee31c2fb
timeCreated: 1504829091
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,612 @@
using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace VRCSDK2.Validation
{
public static class WorldValidation
{
private static readonly Lazy<int> _debugLevel = new Lazy<int>(InitializeLogging);
private static int DebugLevel => _debugLevel.Value;
private static int InitializeLogging()
{
int hashCode = typeof(WorldValidation).GetHashCode();
VRC.Core.Logger.DescribeDebugLevel(hashCode, "WorldValidation", VRC.Core.Logger.Color.red);
VRC.Core.Logger.AddDebugLevel(hashCode);
return hashCode;
}
static string[] ComponentTypeWhiteList = null;
public enum WhiteListConfiguration
{
None,
VRCSDK2,
VRCSDK3,
Unchanged
}
static WhiteListConfiguration ComponentTypeWhiteListConfiguration = WhiteListConfiguration.None;
static readonly string[] ComponentTypeWhiteListCommon = new string[]
{
#if UNITY_STANDALONE
"UnityEngine.Rendering.PostProcessing.PostProcessDebug",
"UnityEngine.Rendering.PostProcessing.PostProcessLayer",
"UnityEngine.Rendering.PostProcessing.PostProcessVolume",
#endif
"VRC.Core.PipelineManager",
"UiInputField",
"VRCProjectSettings",
"DynamicBone",
"DynamicBoneCollider",
"TMPro.TMP_Dropdown",
"TMPro.TMP_InputField",
"TMPro.TMP_ScrollbarEventHandler",
"TMPro.TMP_SelectionCaret",
"TMPro.TMP_SpriteAnimator",
"TMPro.TMP_SubMesh",
"TMPro.TMP_SubMeshUI",
"TMPro.TMP_Text",
"TMPro.TextMeshPro",
"TMPro.TextMeshProUGUI",
"TMPro.TextContainer",
"TMPro.TMP_Dropdown+DropdownItem",
"UnityEngine.EventSystems.EventSystem",
"UnityEngine.EventSystems.EventTrigger",
"UnityEngine.EventSystems.UIBehaviour",
"UnityEngine.EventSystems.BaseInput",
"UnityEngine.EventSystems.BaseInputModule",
"UnityEngine.EventSystems.PointerInputModule",
"UnityEngine.EventSystems.StandaloneInputModule",
"UnityEngine.EventSystems.TouchInputModule",
"UnityEngine.EventSystems.BaseRaycaster",
"UnityEngine.EventSystems.PhysicsRaycaster",
"UnityEngine.UI.Button",
"UnityEngine.UI.Dropdown",
"UnityEngine.UI.Dropdown+DropdownItem",
"UnityEngine.UI.Graphic",
"UnityEngine.UI.GraphicRaycaster",
"UnityEngine.UI.Image",
"UnityEngine.UI.InputField",
"UnityEngine.UI.Mask",
"UnityEngine.UI.MaskableGraphic",
"UnityEngine.UI.RawImage",
"UnityEngine.UI.RectMask2D",
"UnityEngine.UI.Scrollbar",
"UnityEngine.UI.ScrollRect",
"UnityEngine.UI.Selectable",
"UnityEngine.UI.Slider",
"UnityEngine.UI.Text",
"UnityEngine.UI.Toggle",
"UnityEngine.UI.ToggleGroup",
"UnityEngine.UI.AspectRatioFitter",
"UnityEngine.UI.CanvasScaler",
"UnityEngine.UI.ContentSizeFitter",
"UnityEngine.UI.GridLayoutGroup",
"UnityEngine.UI.HorizontalLayoutGroup",
"UnityEngine.UI.HorizontalOrVerticalLayoutGroup",
"UnityEngine.UI.LayoutElement",
"UnityEngine.UI.LayoutGroup",
"UnityEngine.UI.VerticalLayoutGroup",
"UnityEngine.UI.BaseMeshEffect",
"UnityEngine.UI.Outline",
"UnityEngine.UI.PositionAsUV1",
"UnityEngine.UI.Shadow",
"OVRLipSync",
"OVRLipSyncContext",
"OVRLipSyncContextBase",
"OVRLipSyncContextCanned",
"OVRLipSyncContextMorphTarget",
"OVRLipSyncContextTextureFlip",
"ONSPReflectionZone",
"OculusSpatializerUnity",
"ONSPAmbisonicsNative",
"ONSPAudioSource",
"RootMotion.FinalIK.BipedIK",
"RootMotion.FinalIK.FingerRig",
"RootMotion.FinalIK.Grounder",
"RootMotion.FinalIK.GrounderBipedIK",
"RootMotion.FinalIK.GrounderFBBIK",
"RootMotion.FinalIK.GrounderIK",
"RootMotion.FinalIK.GrounderQuadruped",
"RootMotion.FinalIK.GrounderVRIK",
"RootMotion.FinalIK.AimIK",
"RootMotion.FinalIK.CCDIK",
"RootMotion.FinalIK.FABRIK",
"RootMotion.FinalIK.FABRIKRoot",
"RootMotion.FinalIK.FullBodyBipedIK",
"RootMotion.FinalIK.IK",
"RootMotion.FinalIK.IKExecutionOrder",
"RootMotion.FinalIK.LegIK",
"RootMotion.FinalIK.LimbIK",
"RootMotion.FinalIK.LookAtIK",
"RootMotion.FinalIK.TrigonometricIK",
"RootMotion.FinalIK.VRIK",
"RootMotion.FinalIK.FBBIKArmBending",
"RootMotion.FinalIK.FBBIKHeadEffector",
"RootMotion.FinalIK.TwistRelaxer",
"RootMotion.FinalIK.InteractionObject",
"RootMotion.FinalIK.InteractionSystem",
"RootMotion.FinalIK.InteractionTarget",
"RootMotion.FinalIK.InteractionTrigger",
"RootMotion.FinalIK.GenericPoser",
"RootMotion.FinalIK.HandPoser",
"RootMotion.FinalIK.Poser",
"RootMotion.FinalIK.RagdollUtility",
"RootMotion.FinalIK.RotationLimit",
"RootMotion.FinalIK.RotationLimitAngle",
"RootMotion.FinalIK.RotationLimitHinge",
"RootMotion.FinalIK.RotationLimitPolygonal",
"RootMotion.FinalIK.RotationLimitSpline",
"RootMotion.FinalIK.AimPoser",
"RootMotion.FinalIK.Amplifier",
"RootMotion.FinalIK.BodyTilt",
"RootMotion.FinalIK.HitReaction",
"RootMotion.FinalIK.HitReactionVRIK",
"RootMotion.FinalIK.Inertia",
"RootMotion.FinalIK.OffsetModifier",
"RootMotion.FinalIK.OffsetModifierVRIK",
"RootMotion.FinalIK.OffsetPose",
"RootMotion.FinalIK.Recoil",
"RootMotion.FinalIK.ShoulderRotator",
"RootMotion.Dynamics.AnimationBlocker",
"RootMotion.Dynamics.BehaviourBase",
"RootMotion.Dynamics.BehaviourFall",
"RootMotion.Dynamics.BehaviourPuppet",
"RootMotion.Dynamics.JointBreakBroadcaster",
"RootMotion.Dynamics.MuscleCollisionBroadcaster",
"RootMotion.Dynamics.PressureSensor",
"RootMotion.Dynamics.Prop",
"RootMotion.Dynamics.PropRoot",
"RootMotion.Dynamics.PuppetMaster",
"RootMotion.Dynamics.PuppetMasterSettings",
// TODO: remove these if they are only needed in editor
"RootMotion.Dynamics.BipedRagdollCreator",
"RootMotion.Dynamics.RagdollCreator",
"RootMotion.Dynamics.RagdollEditor",
//
"RootMotion.SolverManager",
"RootMotion.TriggerEventBroadcaster",
"UnityEngine.WindZone",
"UnityEngine.Tilemaps.Tilemap",
"UnityEngine.Tilemaps.TilemapRenderer",
"UnityEngine.Terrain",
"UnityEngine.Tree",
"UnityEngine.SpriteMask",
"UnityEngine.Grid",
"UnityEngine.GridLayout",
"UnityEngine.AudioSource",
"UnityEngine.AudioReverbZone",
"UnityEngine.AudioLowPassFilter",
"UnityEngine.AudioHighPassFilter",
"UnityEngine.AudioDistortionFilter",
"UnityEngine.AudioEchoFilter",
"UnityEngine.AudioChorusFilter",
"UnityEngine.AudioReverbFilter",
"UnityEngine.Playables.PlayableDirector",
"UnityEngine.TerrainCollider",
"UnityEngine.Canvas",
"UnityEngine.CanvasGroup",
"UnityEngine.CanvasRenderer",
"UnityEngine.TextMesh",
"UnityEngine.Animator",
"UnityEngine.AI.NavMeshAgent",
"UnityEngine.AI.NavMeshObstacle",
"UnityEngine.AI.OffMeshLink",
"UnityEngine.Cloth",
"UnityEngine.WheelCollider",
"UnityEngine.Rigidbody",
"UnityEngine.Joint",
"UnityEngine.HingeJoint",
"UnityEngine.SpringJoint",
"UnityEngine.FixedJoint",
"UnityEngine.CharacterJoint",
"UnityEngine.ConfigurableJoint",
"UnityEngine.ConstantForce",
"UnityEngine.Collider",
"UnityEngine.BoxCollider",
"UnityEngine.SphereCollider",
"UnityEngine.MeshCollider",
"UnityEngine.CapsuleCollider",
"UnityEngine.CharacterController",
"UnityEngine.ParticleSystem",
"UnityEngine.ParticleSystemRenderer",
"UnityEngine.BillboardRenderer",
"UnityEngine.Camera",
"UnityEngine.FlareLayer",
"UnityEngine.SkinnedMeshRenderer",
"UnityEngine.Renderer",
"UnityEngine.TrailRenderer",
"UnityEngine.LineRenderer",
"UnityEngine.GUIElement",
"UnityEngine.GUILayer",
"UnityEngine.Light",
"UnityEngine.LightProbeGroup",
"UnityEngine.LightProbeProxyVolume",
"UnityEngine.LODGroup",
"UnityEngine.ReflectionProbe",
"UnityEngine.SpriteRenderer",
"UnityEngine.Transform",
"UnityEngine.RectTransform",
"UnityEngine.Rendering.SortingGroup",
"UnityEngine.Projector",
"UnityEngine.OcclusionPortal",
"UnityEngine.OcclusionArea",
"UnityEngine.LensFlare",
"UnityEngine.Skybox",
"UnityEngine.MeshFilter",
"UnityEngine.Halo",
"UnityEngine.MeshRenderer",
"UnityEngine.Collider2D",
"UnityEngine.Rigidbody2D",
"UnityEngine.CompositeCollider2D",
"UnityEngine.ConstantForce2D",
"UnityEngine.AreaEffector2D",
"UnityEngine.CapsuleCollider2D",
"UnityEngine.DistanceJoint2D",
"UnityEngine.EdgeCollider2D",
"UnityEngine.Effector2D",
"UnityEngine.BoxCollider2D",
"UnityEngine.CircleCollider2D",
"UnityEngine.FixedJoint2D",
"UnityEngine.HingeJoint2D",
"UnityEngine.FrictionJoint2D",
"UnityEngine.PlatformEffector2D",
"UnityEngine.PointEffector2D",
"UnityEngine.PolygonCollider2D",
"UnityEngine.SliderJoint2D",
"UnityEngine.SurfaceEffector2D",
"UnityEngine.RelativeJoint2D",
"UnityEngine.TargetJoint2D",
"UnityEngine.WheelJoint2D",
"UnityEngine.Joint2D",
"UnityEngine.ParticleSystemForceField"
};
static readonly string[] ComponentTypeWhiteListSdk2 = new string[]
{
#if UNITY_STANDALONE
"VRCSDK2.VRC_CustomRendererBehaviour",
"VRCSDK2.VRC_MidiNoteIn",
"VRCSDK2.scripts.Scenes.VRC_Panorama",
"VRCSDK2.VRC_Water",
"UnityStandardAssets.Water.WaterBasic",
"UnityStandardAssets.Water.Displace",
"UnityStandardAssets.Water.GerstnerDisplace",
"UnityStandardAssets.Water.PlanarReflection",
"UnityStandardAssets.Water.SpecularLighting",
"UnityStandardAssets.Water.Water",
"UnityStandardAssets.Water.WaterBase",
"UnityStandardAssets.Water.WaterTile",
#endif
"VRCSDK2.VRCTriggerRelay",
"VRCSDK2.VRC_AudioBank",
"VRCSDK2.VRC_DataStorage",
"VRCSDK2.VRC_EventHandler",
"VRCSDK2.VRC_IKFollower",
"VRCSDK2.VRC_Label",
"VRCSDK2.VRC_KeyEvents",
"VRCSDK2.VRC_PhysicsRoot",
"VRCSDK2.VRC_CombatSystem",
"VRCSDK2.VRC_DestructibleStandard",
"VRC_VisualDamage",
"VRCSDK2.VRC_OscButtonIn",
"VRCSDK2.VRC_GunStats",
"VRCSDK2.VRC_JukeBox",
"VRCSDK2.VRC_AddDamage",
"VRCSDK2.VRC_AddHealth",
"VRCSDK2.VRC_AvatarCalibrator",
"VRCSDK2.VRC_AvatarPedestal",
"VRCSDK2.VRC_NPCSpawn",
"VRCSDK2.VRC_ObjectSpawn",
"VRCSDK2.VRC_ObjectSync",
"VRCSDK2.VRC_Pickup",
"VRCSDK2.VRC_PortalMarker",
"VRCSDK2.VRC_SlideShow",
"VRCSDK2.VRC_SpatialAudioSource",
"VRCSDK2.VRC_StationInput",
"VRCSDK2.VRC_SyncAnimation",
"VRCSDK2.VRC_SyncVideoPlayer",
"VRCSDK2.VRC_SyncVideoStream",
"VRCSDK2.VRC_VideoScreen",
"VRCSDK2.VRC_VideoSpeaker",
"VRCSDK2.VRC_PlayerAudioOverride",
"VRCSDK2.VRC_MirrorReflection",
"VRCSDK2.VRC_PlayerMods",
"VRCSDK2.VRC_SceneDescriptor",
"VRCSDK2.VRC_SceneResetPosition",
"VRCSDK2.VRC_SceneSmoothShift",
"VRCSDK2.VRC_SpecialLayer",
"VRCSDK2.VRC_Station",
"VRCSDK2.VRC_StereoObject",
"VRCSDK2.VRC_TimedEvents",
"VRCSDK2.VRC_Trigger",
"VRCSDK2.VRC_TriggerColliderEventTrigger",
"VRCSDK2.VRC_UseEvents",
"VRCSDK2.VRC_UiShape",
"UnityEngine.Animation",
"UnityEngine.GUIText",
"UnityEngine.GUITexture",
"UnityEngine.Video.VideoPlayer",
"PhysSound.PhysSoundBase",
"PhysSound.PhysSoundObject",
"PhysSound.PhysSoundTempAudio",
"PhysSound.PhysSoundTempAudioPool",
"PhysSound.PhysSoundTerrain",
"RealisticEyeMovements.EyeAndHeadAnimator",
"RealisticEyeMovements.LookTargetController",
"UnityStandardAssets.Cameras.AbstractTargetFollower",
"UnityStandardAssets.Cameras.AutoCam",
"UnityStandardAssets.Cameras.FreeLookCam",
"UnityStandardAssets.Cameras.HandHeldCam",
"UnityStandardAssets.Cameras.LookatTarget",
"UnityStandardAssets.Cameras.PivotBasedCameraRig",
"UnityStandardAssets.Cameras.ProtectCameraFromWallClip",
"UnityStandardAssets.Cameras.TargetFieldOfView",
"UnityStandardAssets.Characters.FirstPerson.FirstPersonController",
"UnityStandardAssets.Characters.FirstPerson.HeadBob",
"UnityStandardAssets.Characters.FirstPerson.RigidbodyFirstPersonController",
"UnityStandardAssets.Vehicles.Ball.Ball",
"UnityStandardAssets.Vehicles.Ball.BallUserControl",
"UnityStandardAssets.Characters.ThirdPerson.AICharacterControl",
"UnityStandardAssets.Characters.ThirdPerson.ThirdPersonCharacter",
"UnityStandardAssets.Characters.ThirdPerson.ThirdPersonUserControl",
"UnityStandardAssets.CrossPlatformInput.AxisTouchButton",
"UnityStandardAssets.CrossPlatformInput.ButtonHandler",
"UnityStandardAssets.CrossPlatformInput.InputAxisScrollbar",
"UnityStandardAssets.CrossPlatformInput.Joystick",
"UnityStandardAssets.CrossPlatformInput.MobileControlRig",
"UnityStandardAssets.CrossPlatformInput.TiltInput",
"UnityStandardAssets.CrossPlatformInput.TouchPad",
"UnityStandardAssets.Effects.AfterburnerPhysicsForce",
"UnityStandardAssets.Effects.ExplosionFireAndDebris",
"UnityStandardAssets.Effects.ExplosionPhysicsForce",
"UnityStandardAssets.Effects.Explosive",
"UnityStandardAssets.Effects.ExtinguishableParticleSystem",
"UnityStandardAssets.Effects.FireLight",
"UnityStandardAssets.Effects.Hose",
"UnityStandardAssets.Effects.ParticleSystemMultiplier",
"UnityStandardAssets.Effects.SmokeParticles",
"UnityStandardAssets.Effects.WaterHoseParticles",
"UnityStandardAssets.Utility.ActivateTrigger",
"UnityStandardAssets.Utility.AutoMoveAndRotate",
"UnityStandardAssets.Utility.DragRigidbody",
"UnityStandardAssets.Utility.DynamicShadowSettings",
"UnityStandardAssets.Utility.FollowTarget",
"UnityStandardAssets.Utility.FPSCounter",
"UnityStandardAssets.Utility.ObjectResetter",
"UnityStandardAssets.Utility.ParticleSystemDestroyer",
"UnityStandardAssets.Utility.SimpleActivatorMenu",
"UnityStandardAssets.Utility.SimpleMouseRotator",
"UnityStandardAssets.Utility.SmoothFollow",
"UnityStandardAssets.Utility.TimedObjectActivator",
"UnityStandardAssets.Utility.TimedObjectDestructor",
"UnityStandardAssets.Utility.WaypointCircuit",
"UnityStandardAssets.Utility.WaypointProgressTracker",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAiControl",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneAudio",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneController",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneControlSurfaceAnimator",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplanePropellerAnimator",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl2Axis",
"UnityStandardAssets.Vehicles.Aeroplane.AeroplaneUserControl4Axis",
"UnityStandardAssets.Vehicles.Aeroplane.JetParticleEffect",
"UnityStandardAssets.Vehicles.Aeroplane.LandingGear",
"UnityStandardAssets.Vehicles.Car.BrakeLight",
"UnityStandardAssets.Vehicles.Car.CarAIControl",
"UnityStandardAssets.Vehicles.Car.CarAudio",
"UnityStandardAssets.Vehicles.Car.CarController",
"UnityStandardAssets.Vehicles.Car.CarSelfRighting",
"UnityStandardAssets.Vehicles.Car.CarUserControl",
"UnityStandardAssets.Vehicles.Car.Mudguard",
"UnityStandardAssets.Vehicles.Car.SkidTrail",
"UnityStandardAssets.Vehicles.Car.Suspension",
"UnityStandardAssets.Vehicles.Car.WheelEffects",
"RenderHeads.Media.AVProVideo.ApplyToMaterial",
"RenderHeads.Media.AVProVideo.ApplyToMesh",
"RenderHeads.Media.AVProVideo.AudioOutput",
"RenderHeads.Media.AVProVideo.CubemapCube",
"RenderHeads.Media.AVProVideo.DebugOverlay",
"RenderHeads.Media.AVProVideo.DisplayBackground",
"RenderHeads.Media.AVProVideo.DisplayIMGUI",
"RenderHeads.Media.AVProVideo.DisplayUGUI",
"RenderHeads.Media.AVProVideo.MediaPlayer",
"RenderHeads.Media.AVProVideo.StreamParser",
"RenderHeads.Media.AVProVideo.SubtitlesUGUI",
"RenderHeads.Media.AVProVideo.UpdateStereoMaterial",
"AlphaButtonClickMask",
"EventSystemChecker",
"VirtualMarketplaceItem",
"SDK2UrlLauncher"
};
static readonly string[] ComponentTypeWhiteListSdk3 = new string[]
{
"VRC.SDK3.VRCDestructibleStandard",
"VRC.SDK3.Components.VRCVisualDamage",
"VRC.SDK3.Components.VRCAvatarPedestal",
"VRC.SDK3.Components.VRCPickup",
"VRC.SDK3.Components.VRCPortalMarker",
"VRC.SDK3.Components.VRCSpatialAudioSource",
"VRC.SDK3.Components.VRCMirrorReflection",
"VRC.SDK3.Components.VRCSceneDescriptor",
"VRC.SDK3.Components.VRCStation",
"VRC.SDK3.Components.VRCUiShape",
"VRC.SDK3.Video.Components.VRCUnityVideoPlayer",
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoPlayer",
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoScreen",
"VRC.SDK3.Video.Components.AVPro.VRCAVProVideoSpeaker",
"VRC.Udon.UdonBehaviour",
"UnityEngine.Animations.AimConstraint",
"UnityEngine.Animations.LookAtConstraint",
"UnityEngine.Animations.ParentConstraint",
"UnityEngine.Animations.PositionConstraint",
"UnityEngine.Animations.RotationConstraint",
"UnityEngine.Animations.ScaleConstraint",
"UnityEngine.ParticleSystemForceField",
"Cinemachine.Cinemachine3rdPersonAim",
"Cinemachine.CinemachineBlendListCamera",
"Cinemachine.CinemachineBrain",
"Cinemachine.CinemachineCameraOffset",
"Cinemachine.CinemachineClearShot",
"Cinemachine.CinemachineCollider",
"Cinemachine.CinemachineConfiner",
"Cinemachine.CinemachineDollyCart",
"Cinemachine.CinemachineExternalCamera",
"Cinemachine.CinemachineFollowZoom",
"Cinemachine.CinemachineFreeLook",
"Cinemachine.CinemachineMixingCamera",
"Cinemachine.CinemachinePath",
"Cinemachine.CinemachinePipeline",
"Cinemachine.CinemachinePixelPerfect",
"Cinemachine.CinemachineRecomposer",
"Cinemachine.CinemachineSmoothPath",
"Cinemachine.CinemachineStateDrivenCamera",
"Cinemachine.CinemachineStoryboard",
"Cinemachine.CinemachineTargetGroup",
"Cinemachine.CinemachineVirtualCamera",
"Cinemachine.Cinemachine3rdPersonFollow",
"Cinemachine.CinemachineBasicMultiChannelPerlin",
"Cinemachine.CinemachineComposer",
"Cinemachine.CinemachineFramingTransposer",
"Cinemachine.CinemachineGroupComposer",
"Cinemachine.CinemachineHardLockToTarget",
"Cinemachine.CinemachineHardLookAt",
"Cinemachine.CinemachineOrbitalTransposer",
"Cinemachine.CinemachinePOV",
"Cinemachine.CinemachineSameAsFollowTarget",
"Cinemachine.CinemachineTrackedDolly",
"Cinemachine.CinemachineTransposer",
"Cinemachine.CinemachineCore"
};
public static readonly string[] ShaderWhiteList = new string[]
{
"VRChat/Mobile/Standard Lite",
"VRChat/Mobile/Diffuse",
"VRChat/Mobile/Bumped Diffuse",
"VRChat/Mobile/Bumped Mapped Specular",
"VRChat/Mobile/Toon Lit",
"VRChat/Mobile/MatCap Lit",
"VRChat/Mobile/Lightmapped",
"VRChat/Mobile/Skybox",
"VRChat/Mobile/Particles/Additive",
"VRChat/Mobile/Particles/Multiply",
"FX/MirrorReflection",
"UI/Default",
};
private static readonly HashSet<int> scannedObjects = new HashSet<int>();
private static void ConfigureWhiteList(WhiteListConfiguration config)
{
if (ComponentTypeWhiteListConfiguration == config ||
config == WhiteListConfiguration.Unchanged)
{
return;
}
List<string> concatenation = new List<string>();
concatenation.AddRange(ComponentTypeWhiteListCommon);
switch(config)
{
case WhiteListConfiguration.VRCSDK2:
concatenation.AddRange(ComponentTypeWhiteListSdk2);
break;
case WhiteListConfiguration.VRCSDK3:
concatenation.AddRange(ComponentTypeWhiteListSdk3);
break;
}
ComponentTypeWhiteListConfiguration = config;
ComponentTypeWhiteList = concatenation.ToArray();
}
[PublicAPI]
public static void RemoveIllegalComponents(IEnumerable<GameObject> targets, WhiteListConfiguration config, bool retry = true)
{
ConfigureWhiteList(config);
HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes("world" + config, ComponentTypeWhiteList);
foreach(GameObject target in targets)
{
ValidationUtils.RemoveIllegalComponents(target, whitelist, retry, true);
SecurityScan(target);
AddScanned(target);
}
}
private static void AddScanned(GameObject obj)
{
if (obj == null)
return;
if (!scannedObjects.Contains(obj.GetInstanceID()))
scannedObjects.Add(obj.GetInstanceID());
for (int idx = 0; idx < obj.transform.childCount; ++idx)
AddScanned(obj.transform.GetChild(idx)?.gameObject);
}
private static bool WasScanned(GameObject obj)
{
return scannedObjects.Contains(obj.GetInstanceID());
}
[PublicAPI]
public static void ScanGameObject(GameObject target, WhiteListConfiguration config)
{
if (WasScanned(target))
{
return;
}
ConfigureWhiteList(config);
HashSet<Type> whitelist = ValidationUtils.WhitelistedTypes("world" + config, ComponentTypeWhiteList);
ValidationUtils.RemoveIllegalComponents(target, whitelist);
SecurityScan(target);
AddScanned(target);
}
[PublicAPI]
public static void ClearScannedGameObjectCache()
{
scannedObjects.Clear();
}
[PublicAPI]
public static IEnumerable<Shader> FindIllegalShaders(GameObject target)
{
return ShaderValidation.FindIllegalShaders(target, ShaderWhiteList);
}
private static void SecurityScan(GameObject target)
{
PlayableDirector[] playabledirectors = target.GetComponentsInChildren<PlayableDirector>(true);
foreach (PlayableDirector playableDirector in playabledirectors)
StripPlayableDirectorWithPrefabs(playableDirector);
}
private static void StripPlayableDirectorWithPrefabs(PlayableDirector playableDirector)
{
if (!(playableDirector.playableAsset is UnityEngine.Timeline.TimelineAsset timelineAsset))
return;
IEnumerable<TrackAsset> tracks = timelineAsset.GetOutputTracks();
foreach (TrackAsset track in tracks)
{
if (!(track is ControlTrack))
continue;
IEnumerable<TimelineClip> clips = track.GetClips();
foreach (TimelineClip clip in clips)
{
if (clip.asset is ControlPlayableAsset controlPlayableAsset && controlPlayableAsset.prefabGameObject != null)
{
UnityEngine.Object.Destroy(playableDirector);
VRC.Core.Logger.LogWarning("PlayableDirector containing prefab removed", DebugLevel, playableDirector.gameObject);
}
}
}
}
}
}
@@ -0,0 +1,12 @@
fileFormatVersion: 2
guid: 9b03724cd556cb047b2da80492ea28a5
timeCreated: 1504829091
licenseType: Pro
MonoImporter:
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 178bc3d6d6cc3ca4897436bc22109f96
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,968 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using JetBrains.Annotations;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityEngine.Video;
#if VRC_SDK_VRCSDK3
using VRC.SDK3.Components;
using TMPro;
#endif
#if UDON
using VRC.Udon;
#endif
#if !VRC_CLIENT && UNITY_EDITOR && VRC_SDK_VRCSDK3
using UnityEditor;
using UnityEngine.SceneManagement;
#endif
namespace VRC.Core
{
public class UnityEventFilter
{
// These types are will always be prohibited even if they are derived from an allowed type.
private static readonly HashSet<Type> _prohibitedUIEventTargetTypes = new HashSet<Type>
{
#if VRC_CLIENT
typeof(RenderHeads.Media.AVProVideo.MediaPlayer),
#endif
#if VRC_SDK_VRCSDK3
typeof(VRCUrlInputField),
#endif
typeof(VideoPlayer)
};
private static readonly Lazy<Dictionary<Type, AllowedMethodFilter>> _allowedUnityEventTargetTypes =
new Lazy<Dictionary<Type, AllowedMethodFilter>>(GetRuntimeUnityEventTargetAccessFilterDictionary);
private static Dictionary<Type, AllowedMethodFilter> AllowedUnityEventTargetTypes => _allowedUnityEventTargetTypes.Value;
private static readonly Lazy<int> _debugLevel = new Lazy<int>(InitializeLogging);
private static int DebugLevel => _debugLevel.Value;
// Builds a HashSet of allowed types, and their derived types, and removes explicitly prohibited types.
private static Dictionary<Type, AllowedMethodFilter> GetRuntimeUnityEventTargetAccessFilterDictionary()
{
Dictionary<Type, AllowedMethodFilter> accessFilterDictionary = new Dictionary<Type, AllowedMethodFilter>(_initialTargetAccessFilters);
AddDerivedTypes(accessFilterDictionary);
RemoveProhibitedTypes(accessFilterDictionary);
#if VERBOSE_EVENT_SANITIZATION_LOGGING
StringBuilder stringBuilder = new StringBuilder();
foreach(KeyValuePair<Type, AllowedMethodFilter> entry in accessFilterDictionary)
{
stringBuilder.AppendLine(entry.Key.FullName);
AllowedMethodFilter targetMethodAccessFilter = entry.Value;
foreach(string targetMethod in targetMethodAccessFilter.GetTargetMethodNames())
{
stringBuilder.AppendLine($" {targetMethod}");
}
stringBuilder.AppendLine();
}
VerboseLog(stringBuilder.ToString());
#endif
return accessFilterDictionary;
}
#if !VRC_CLIENT && UNITY_EDITOR && VRC_SDK_VRCSDK3
[RuntimeInitializeOnLoadMethod]
private static void SetupPlayMode()
{
EditorApplication.playModeStateChanged += RunFilteringOnPlayModeEntry;
}
private static void RunFilteringOnPlayModeEntry(PlayModeStateChange playModeStateChange)
{
switch(playModeStateChange)
{
case PlayModeStateChange.EnteredPlayMode:
{
for(int sceneIndex = 0; sceneIndex < SceneManager.sceneCount; sceneIndex++)
{
Scene currentScene = SceneManager.GetSceneAt(sceneIndex);
List<GameObject> rootGameObjects = new List<GameObject>();
currentScene.GetRootGameObjects(rootGameObjects);
FilterUIEvents(rootGameObjects);
FilterAnimatorEvents(rootGameObjects);
}
break;
}
case PlayModeStateChange.EnteredEditMode:
case PlayModeStateChange.ExitingEditMode:
case PlayModeStateChange.ExitingPlayMode:
{
return;
}
default:
{
throw new ArgumentOutOfRangeException(nameof(playModeStateChange), playModeStateChange, null);
}
}
}
#endif
private static int InitializeLogging()
{
int hashCode = typeof(UnityEventFilter).GetHashCode();
Logger.DescribeDebugLevel(hashCode, "UnityEventFilter", Logger.Color.red);
Logger.AddDebugLevel(hashCode);
return hashCode;
}
[PublicAPI]
public static void FilterUIEvents(GameObject gameObject)
{
List<UIBehaviour> uiBehaviours = new List<UIBehaviour>();
gameObject.GetComponentsInChildren(true, uiBehaviours);
FilterUIBehaviourEvents(uiBehaviours);
}
[PublicAPI]
public static void FilterUIEvents(List<GameObject> gameObjects)
{
HashSet<UIBehaviour> uiBehaviours = new HashSet<UIBehaviour>();
List<UIBehaviour> uiBehavioursWorkingList = new List<UIBehaviour>();
foreach(GameObject gameObject in gameObjects)
{
gameObject.GetComponentsInChildren(true, uiBehavioursWorkingList);
uiBehaviours.UnionWith(uiBehavioursWorkingList);
}
FilterUIBehaviourEvents(uiBehaviours);
}
[PublicAPI]
public static void FilterAnimatorEvents(GameObject gameObject)
{
List<Animator> animators = new List<Animator>();
gameObject.GetComponentsInChildren(true, animators);
FilterAnimatorEvents(animators);
}
[PublicAPI]
public static void FilterAnimatorEvents(List<GameObject> gameObjects)
{
HashSet<Animator> animators = new HashSet<Animator>();
List<Animator> animatorsWorkingList = new List<Animator>();
foreach(GameObject gameObject in gameObjects)
{
gameObject.GetComponentsInChildren(true, animatorsWorkingList);
animators.UnionWith(animatorsWorkingList);
}
FilterAnimatorEvents(animators);
}
private static void FilterUIBehaviourEvents(IEnumerable<UIBehaviour> uiBehaviours)
{
Dictionary<Type, List<UIBehaviour>> uiBehavioursByType = new Dictionary<Type, List<UIBehaviour>>();
foreach(UIBehaviour uiBehaviour in uiBehaviours)
{
if(uiBehaviour == null)
{
continue;
}
Type uiBehaviourType = uiBehaviour.GetType();
if(!uiBehavioursByType.TryGetValue(uiBehaviourType, out List<UIBehaviour> uiBehavioursOfType))
{
uiBehavioursByType.Add(uiBehaviourType, new List<UIBehaviour> {uiBehaviour});
continue;
}
uiBehavioursOfType.Add(uiBehaviour);
}
foreach(KeyValuePair<Type, List<UIBehaviour>> uiBehavioursOfTypeKvp in uiBehavioursByType)
{
Type uiBehaviourType = uiBehavioursOfTypeKvp.Key;
FieldInfo[] fieldInfos = uiBehaviourType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
List<FieldInfo> unityEventFieldInfos = new List<FieldInfo>();
foreach(FieldInfo fieldInfo in fieldInfos)
{
if(typeof(UnityEventBase).IsAssignableFrom(fieldInfo.FieldType))
{
unityEventFieldInfos.Add(fieldInfo);
}
}
if(unityEventFieldInfos.Count <= 0)
{
continue;
}
FieldInfo persistentCallsGroupFieldInfo = typeof(UnityEventBase).GetField("m_PersistentCalls", BindingFlags.Instance | BindingFlags.NonPublic);
if(persistentCallsGroupFieldInfo == null)
{
VerboseLog($"Could not find 'm_PersistentCalls' on UnityEventBase.");
return;
}
foreach(UIBehaviour uiBehaviour in uiBehavioursOfTypeKvp.Value)
{
VerboseLog($"Checking '{uiBehaviour.name} for UI Events.", uiBehaviour);
foreach(FieldInfo unityEventFieldInfo in unityEventFieldInfos)
{
VerboseLog($"Checking field '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}.", uiBehaviour);
UnityEventBase unityEventBase = unityEventFieldInfo.GetValue(uiBehaviour) as UnityEventBase;
if(unityEventBase == null)
{
VerboseLog($"Null '{unityEventFieldInfo.Name}' UnityEvent on {uiBehaviour.name}.", uiBehaviour);
continue;
}
int numEventListeners = unityEventBase.GetPersistentEventCount();
VerboseLog($"There are '{numEventListeners}' on event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}.", uiBehaviour);
for(int index = 0; index < numEventListeners; index++)
{
string persistentMethodName = unityEventBase.GetPersistentMethodName(index);
UnityEngine.Object persistentTarget = unityEventBase.GetPersistentTarget(index);
if(persistentTarget == null)
{
VerboseLog($"The target for listener '{index}' on event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name} is null.", uiBehaviour);
continue;
}
if(IsTargetPermitted(persistentTarget, persistentMethodName))
{
VerboseLog(
$"Allowing event '{unityEventFieldInfo.Name}' on '{uiBehaviour.name}' to call '{persistentMethodName}' on target '{persistentTarget.name}'.",
uiBehaviour);
continue;
}
LogRemoval(
$"Events on '{uiBehaviour.name}' were removed because one of them targeted a prohibited type '{persistentTarget.GetType().Name}', method '{persistentMethodName}' or object '{persistentTarget.name}'.",
uiBehaviour);
unityEventFieldInfo.SetValue(uiBehaviour, Activator.CreateInstance(unityEventBase.GetType()));
break;
}
}
}
}
}
private static void FilterAnimatorEvents(IEnumerable<Animator> animators)
{
foreach(Animator animator in animators)
{
if(animator == null)
{
continue;
}
RuntimeAnimatorController animatorController = animator.runtimeAnimatorController;
if(animatorController == null)
{
return;
}
foreach(AnimationClip animationClip in animatorController.animationClips)
{
if(animationClip == null)
{
continue;
}
foreach(AnimationEvent animationEvent in animationClip.events)
{
if(animationEvent == null)
{
continue;
}
string animationEventFunctionName = animationEvent.functionName;
if(_allowedAnimationEventFunctionNames.Contains(animationEventFunctionName))
{
continue;
}
animationClip.events = null;
LogRemoval(
$"Removed AnimationEvents from AnimationClip used by the Animator on '{animator.gameObject}' because the event targets '{animationEventFunctionName}' which is not allowed.");
break;
}
}
}
}
[Conditional("VERBOSE_EVENT_SANITIZATION_LOGGING")]
private static void VerboseLog(string message, UnityEngine.Object target = null)
{
Logger.LogWarning(message, DebugLevel, target);
}
private static void LogRemoval(string message, UnityEngine.Object target = null)
{
Logger.LogWarning(message, DebugLevel, target);
}
private static bool IsTargetPermitted(UnityEngine.Object target, string targetMethod)
{
// Block anything blacklisted by Udon to prevent UnityEvents from being used to bypass the blacklist.
// NOTE: This will only block events targeting objects that are blacklisted before the UnityEventSanitizer is run.
// If objects are added to the blacklist after scene loading has finished it will be necessary to re-run the UnityEventSanitizer.
#if UDON
if(UdonManager.Instance.IsBlacklisted(target))
{
return false;
}
#endif
Type persistentTargetType = target.GetType();
if(!AllowedUnityEventTargetTypes.TryGetValue(persistentTargetType, out AllowedMethodFilter accessFilter))
{
return false;
}
return accessFilter.IsTargetMethodAllowed(targetMethod);
}
// Adds types derived from whitelisted types.
private static void AddDerivedTypes(Dictionary<Type, AllowedMethodFilter> accessFilterDictionary)
{
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach(Assembly assembly in assemblies)
{
foreach(Type type in assembly.GetTypes())
{
if(accessFilterDictionary.ContainsKey(type))
{
continue;
}
if(!typeof(Component).IsAssignableFrom(type))
{
continue;
}
Type currentType = type;
while(currentType != typeof(object) && currentType != null)
{
if(accessFilterDictionary.TryGetValue(currentType, out AllowedMethodFilter accessFilter))
{
accessFilterDictionary.Add(type, accessFilter);
break;
}
currentType = currentType.BaseType;
}
}
}
}
// Removes prohibited types and types derived from them.
private static void RemoveProhibitedTypes(Dictionary<Type, AllowedMethodFilter> accessFilterDictionary)
{
foreach(Type prohibitedType in _prohibitedUIEventTargetTypes)
{
foreach(Type accessFilterType in accessFilterDictionary.Keys.ToArray())
{
if(prohibitedType.IsAssignableFrom(accessFilterType))
{
accessFilterDictionary.Remove(accessFilterType);
}
}
}
}
private static readonly Dictionary<Type, AllowedMethodFilter> _initialTargetAccessFilters = new Dictionary<Type, AllowedMethodFilter>
{
{
typeof(GameObject), new AllowedMethodFilter(
new List<string>
{
nameof(GameObject.SetActive)
},
new List<string>())
},
{
typeof(AudioSource),
new AllowedMethodFilter(
new List<string>
{
nameof(AudioSource.Pause),
nameof(AudioSource.Play),
nameof(AudioSource.PlayDelayed),
nameof(AudioSource.PlayOneShot),
nameof(AudioSource.Stop),
nameof(AudioSource.UnPause)
},
new List<string>
{
nameof(AudioSource.bypassEffects),
nameof(AudioSource.bypassListenerEffects),
nameof(AudioSource.bypassReverbZones),
nameof(AudioSource.dopplerLevel),
nameof(AudioSource.enabled),
nameof(AudioSource.loop),
nameof(AudioSource.maxDistance),
nameof(AudioSource.rolloffMode),
nameof(AudioSource.minDistance),
nameof(AudioSource.mute),
nameof(AudioSource.pitch),
nameof(AudioSource.playOnAwake),
nameof(AudioSource.priority),
nameof(AudioSource.spatialize),
nameof(AudioSource.spread),
nameof(AudioSource.time),
nameof(AudioSource.volume)
}
)
},
{
typeof(AudioDistortionFilter), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioDistortionFilter.distortionLevel),
nameof(AudioDistortionFilter.enabled)
})
},
{
typeof(AudioEchoFilter), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioEchoFilter.decayRatio),
nameof(AudioEchoFilter.delay),
nameof(AudioEchoFilter.dryMix),
nameof(AudioEchoFilter.enabled),
nameof(AudioEchoFilter.wetMix)
})
},
{
typeof(AudioHighPassFilter), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioHighPassFilter.cutoffFrequency),
nameof(AudioHighPassFilter.enabled),
nameof(AudioHighPassFilter.highpassResonanceQ)
})
},
{
typeof(AudioLowPassFilter), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioLowPassFilter.cutoffFrequency),
nameof(AudioLowPassFilter.enabled),
nameof(AudioLowPassFilter.lowpassResonanceQ)
})
},
{
typeof(AudioReverbFilter), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioReverbFilter.decayHFRatio),
nameof(AudioReverbFilter.decayTime),
nameof(AudioReverbFilter.density),
nameof(AudioReverbFilter.diffusion),
nameof(AudioReverbFilter.dryLevel),
nameof(AudioReverbFilter.enabled),
nameof(AudioReverbFilter.hfReference),
nameof(AudioReverbFilter.reflectionsDelay),
nameof(AudioReverbFilter.reflectionsLevel),
nameof(AudioReverbFilter.reverbDelay),
nameof(AudioReverbFilter.reverbLevel),
nameof(AudioReverbFilter.room),
nameof(AudioReverbFilter.roomHF),
nameof(AudioReverbFilter.roomLF)
})
},
{
typeof(AudioReverbZone), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(AudioReverbZone.decayHFRatio),
nameof(AudioReverbZone.decayTime),
nameof(AudioReverbZone.density),
nameof(AudioReverbZone.diffusion),
nameof(AudioReverbZone.enabled),
nameof(AudioReverbZone.HFReference),
nameof(AudioReverbZone.LFReference),
nameof(AudioReverbZone.maxDistance),
nameof(AudioReverbZone.minDistance),
nameof(AudioReverbZone.reflections),
nameof(AudioReverbZone.reflectionsDelay),
nameof(AudioReverbZone.room),
nameof(AudioReverbZone.roomHF),
nameof(AudioReverbZone.roomLF)
})
},
#if UDON
{
typeof(UdonBehaviour), new AllowedMethodFilter(
new List<string>
{
nameof(UdonBehaviour.RunProgram),
nameof(UdonBehaviour.SendCustomEvent),
nameof(UdonBehaviour.Interact),
},
new List<string>()
{
nameof(UdonBehaviour.enabled)
})
},
#endif
{
typeof(MeshRenderer), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(MeshRenderer.shadowCastingMode),
nameof(MeshRenderer.enabled),
nameof(MeshRenderer.probeAnchor),
nameof(MeshRenderer.probeAnchor),
nameof(MeshRenderer.receiveShadows),
nameof(MeshRenderer.lightProbeUsage)
})
},
{
typeof(Collider), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Collider.enabled),
nameof(Collider.isTrigger)
})
},
{
typeof(SkinnedMeshRenderer), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(SkinnedMeshRenderer.allowOcclusionWhenDynamic),
nameof(SkinnedMeshRenderer.shadowCastingMode),
nameof(SkinnedMeshRenderer.enabled),
nameof(SkinnedMeshRenderer.lightProbeProxyVolumeOverride),
nameof(SkinnedMeshRenderer.motionVectorGenerationMode),
nameof(SkinnedMeshRenderer.probeAnchor),
nameof(SkinnedMeshRenderer.receiveShadows),
nameof(SkinnedMeshRenderer.rootBone),
nameof(SkinnedMeshRenderer.skinnedMotionVectors),
nameof(SkinnedMeshRenderer.updateWhenOffscreen),
nameof(SkinnedMeshRenderer.lightProbeUsage)
})
},
{
typeof(Light), new AllowedMethodFilter(
new List<string>
{
nameof(Light.Reset)
},
new List<string>
{
nameof(Light.bounceIntensity),
nameof(Light.colorTemperature),
nameof(Light.cookie),
nameof(Light.enabled),
nameof(Light.intensity),
nameof(Light.range),
nameof(Light.shadowBias),
nameof(Light.shadowNearPlane),
nameof(Light.shadowNormalBias),
nameof(Light.shadowStrength),
nameof(Light.spotAngle)
})
},
{
typeof(ParticleSystem), new AllowedMethodFilter(
new List<string>
{
nameof(ParticleSystem.Clear),
nameof(ParticleSystem.Emit),
nameof(ParticleSystem.Pause),
nameof(ParticleSystem.Pause),
nameof(ParticleSystem.Play),
nameof(ParticleSystem.Simulate),
nameof(ParticleSystem.Stop),
nameof(ParticleSystem.Stop),
nameof(ParticleSystem.TriggerSubEmitter)
},
new List<string>
{
nameof(ParticleSystem.time),
nameof(ParticleSystem.useAutoRandomSeed)
})
},
{
typeof(ParticleSystemForceField), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(ParticleSystemForceField.endRange),
nameof(ParticleSystemForceField.gravityFocus),
nameof(ParticleSystemForceField.length),
nameof(ParticleSystemForceField.multiplyDragByParticleSize),
nameof(ParticleSystemForceField.multiplyDragByParticleVelocity),
nameof(ParticleSystemForceField.startRange)
})
},
{
typeof(Projector), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Projector.aspectRatio),
nameof(Projector.enabled),
nameof(Projector.nearClipPlane),
nameof(Projector.farClipPlane),
nameof(Projector.fieldOfView),
nameof(Projector.orthographic),
nameof(Projector.orthographicSize)
})
},
{
typeof(LineRenderer), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(LineRenderer.allowOcclusionWhenDynamic),
nameof(LineRenderer.shadowCastingMode),
nameof(LineRenderer.enabled),
nameof(LineRenderer.endWidth),
nameof(LineRenderer.loop),
nameof(LineRenderer.motionVectorGenerationMode),
nameof(LineRenderer.numCapVertices),
nameof(LineRenderer.numCornerVertices),
nameof(LineRenderer.probeAnchor),
nameof(LineRenderer.receiveShadows),
nameof(LineRenderer.shadowBias),
nameof(LineRenderer.startWidth),
nameof(LineRenderer.lightProbeUsage),
nameof(LineRenderer.useWorldSpace),
nameof(LineRenderer.widthMultiplier)
})
},
{
typeof(TrailRenderer), new AllowedMethodFilter(
new List<string>
{
nameof(TrailRenderer.Clear)
},
new List<string>
{
nameof(TrailRenderer.allowOcclusionWhenDynamic),
nameof(TrailRenderer.autodestruct),
nameof(TrailRenderer.shadowCastingMode),
nameof(TrailRenderer.enabled),
nameof(TrailRenderer.emitting),
nameof(TrailRenderer.endWidth),
nameof(TrailRenderer.motionVectorGenerationMode),
nameof(TrailRenderer.numCapVertices),
nameof(TrailRenderer.numCornerVertices),
nameof(TrailRenderer.probeAnchor),
nameof(TrailRenderer.receiveShadows),
nameof(TrailRenderer.shadowBias),
nameof(TrailRenderer.startWidth),
nameof(TrailRenderer.lightProbeUsage),
nameof(TrailRenderer.widthMultiplier)
})
},
{
typeof(Animator), new AllowedMethodFilter(
new List<string>
{
nameof(Animator.Play),
nameof(Animator.PlayInFixedTime),
nameof(Animator.Rebind),
nameof(Animator.SetBool),
nameof(Animator.SetFloat),
nameof(Animator.SetInteger),
nameof(Animator.SetTrigger),
nameof(Animator.ResetTrigger)
},
new List<string>
{
nameof(Animator.speed),
nameof(Animator.enabled)
})
},
{
typeof(Text), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Text.alignByGeometry),
nameof(Text.enabled),
nameof(Text.fontSize),
nameof(Text.lineSpacing),
nameof(Text.maskable),
nameof(Text.raycastTarget),
nameof(Text.resizeTextForBestFit),
nameof(Text.resizeTextMaxSize),
nameof(Text.resizeTextMinSize),
nameof(Text.supportRichText),
nameof(Text.text)
})
},
{
typeof(Image), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Image.alphaHitTestMinimumThreshold),
nameof(Image.enabled),
nameof(Image.fillAmount),
nameof(Image.fillCenter),
nameof(Image.fillClockwise),
nameof(Image.fillOrigin),
nameof(Image.maskable),
nameof(Image.preserveAspect),
nameof(Image.raycastTarget),
nameof(Image.useSpriteMesh)
})
},
{
typeof(RawImage), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(RawImage.enabled),
nameof(RawImage.maskable),
nameof(RawImage.raycastTarget)
})
},
{
typeof(InputField), new AllowedMethodFilter(
new List<string>
{
"Append",
nameof(InputField.ForceLabelUpdate)
},
new List<string>
{
nameof(InputField.caretBlinkRate),
nameof(InputField.caretPosition),
nameof(InputField.caretWidth),
nameof(InputField.characterLimit),
nameof(InputField.customCaretColor),
nameof(InputField.enabled),
nameof(InputField.interactable),
nameof(InputField.readOnly),
nameof(InputField.selectionAnchorPosition),
nameof(InputField.text),
nameof(InputField.textComponent),
nameof(InputField.selectionFocusPosition)
})
},
{
typeof(Dropdown), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Dropdown.captionText),
nameof(Dropdown.enabled),
nameof(Dropdown.interactable),
nameof(Dropdown.itemText),
nameof(Dropdown.targetGraphic),
nameof(Dropdown.template),
nameof(Dropdown.value)
})
},
{
typeof(Slider), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Slider.enabled),
nameof(Slider.fillRect),
nameof(Slider.handleRect),
nameof(Slider.interactable),
nameof(Slider.maxValue),
nameof(Slider.minValue),
nameof(Slider.normalizedValue),
nameof(Slider.targetGraphic),
nameof(Slider.value),
nameof(Slider.wholeNumbers)
})
},
{
typeof(Toggle), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Toggle.enabled),
nameof(Toggle.group),
nameof(Toggle.interactable),
nameof(Toggle.isOn),
nameof(Toggle.targetGraphic)
})
},
{
typeof(Scrollbar), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Scrollbar.enabled),
nameof(Scrollbar.handleRect),
nameof(Scrollbar.interactable),
nameof(Scrollbar.numberOfSteps),
nameof(Scrollbar.size),
nameof(Scrollbar.targetGraphic),
nameof(Scrollbar.value)
})
},
{
typeof(ScrollRect), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(ScrollRect.content),
nameof(ScrollRect.decelerationRate),
nameof(ScrollRect.elasticity),
nameof(ScrollRect.enabled),
nameof(ScrollRect.horizontal),
nameof(ScrollRect.horizontalNormalizedPosition),
nameof(ScrollRect.horizontalScrollbar),
nameof(ScrollRect.horizontalScrollbarSpacing),
nameof(ScrollRect.inertia),
nameof(ScrollRect.scrollSensitivity),
nameof(ScrollRect.vertical),
nameof(ScrollRect.verticalNormalizedPosition),
nameof(ScrollRect.verticalScrollbar),
nameof(ScrollRect.verticalScrollbarSpacing),
nameof(ScrollRect.viewport)
})
},
{
typeof(Button), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Button.enabled),
nameof(Button.interactable),
nameof(Button.targetGraphic)
})
},
{
typeof(Mask), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Mask.enabled),
nameof(Mask.showMaskGraphic)
})
},
{
typeof(RectMask2D), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(RectMask2D.enabled)
})
},
{
typeof(Selectable), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(Selectable.enabled),
nameof(Selectable.interactable),
nameof(Selectable.targetGraphic)
})
},
{
typeof(ToggleGroup), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(ToggleGroup.allowSwitchOff),
nameof(ToggleGroup.enabled)
})
},
#if VRC_SDK_VRCSDK3 // only access Cinemachine and TMPro after install
{
typeof(TextMeshPro), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(TextMeshPro.text),
})
},
{
typeof(TextMeshProUGUI), new AllowedMethodFilter(
new List<string>(),
new List<string>
{
nameof(TextMeshProUGUI.text),
})
},
{
typeof(Cinemachine.CinemachineVirtualCamera), new AllowedMethodFilter(
new List<string>()
{
nameof(Cinemachine.CinemachineVirtualCamera.Priority),
},
new List<string>())
},
#endif
};
private static readonly HashSet<string> _allowedAnimationEventFunctionNames = new HashSet<string>
{
"RunProgram",
"SendCustomEvent",
"Play",
"Pause",
"Stop",
"PlayInFixedTime",
"Rebind",
"SetBool",
"SetFloat",
"SetInteger",
"SetTrigger",
"ResetTrigger",
"SetActive"
};
private class AllowedMethodFilter
{
private readonly HashSet<string> _allowedTargets;
[PublicAPI]
public AllowedMethodFilter(List<string> allowedTargetMethodNames, List<string> allowedTargetPropertyNames)
{
_allowedTargets = new HashSet<string>();
_allowedTargets.UnionWith(allowedTargetMethodNames);
foreach(string allowedTargetProperty in allowedTargetPropertyNames)
{
_allowedTargets.Add($"get_{allowedTargetProperty}");
_allowedTargets.Add($"set_{allowedTargetProperty}");
}
}
public bool IsTargetMethodAllowed(string targetMethodName)
{
return _allowedTargets.Contains(targetMethodName);
}
#if VERBOSE_EVENT_SANITIZATION_LOGGING
public List<string> GetTargetMethodNames()
{
return _allowedTargets.ToList();
}
#endif
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 87f13d6a98e54fb78ca47650a29029a5
timeCreated: 1594690552