public static Dictionary <ParticleSystem, int> EnforceParticleSystemLimits(GameObject currentAvatar)
        {
            Dictionary <ParticleSystem, int> particleSystems = new Dictionary <ParticleSystem, int>();

            foreach (ParticleSystem ps in currentAvatar.transform.GetComponentsInChildren <ParticleSystem>(true))
            {
                int realtime_max = ps_max_particles;

                // always limit collision force
                var collision = ps.collision;
                if (collision.colliderForce > ps_max_particle_force)
                {
                    collision.colliderForce = ps_max_particle_force;
                    Debug.LogError("Collision force is restricted on avatars, particle system named " + ps.gameObject.name + " collision force restricted to " + ps_max_particle_force);
                }

                if (ps_limiter_enabled)
                {
                    if (particleSystems.Count > ps_max_systems)
                    {
                        Debug.LogError("Too many particle systems, #" + particleSystems.Count + " named " + ps.gameObject.name + " deleted");
                        ValidationUtils.RemoveComponent(ps);
                        continue;
                    }
                    else
                    {
                        var main     = ps.main;
                        var emission = ps.emission;

                        ParticleSystemRenderer renderer = ps.GetComponent <ParticleSystemRenderer>();
                        if (renderer != null)
                        {
                            if (renderer.renderMode == ParticleSystemRenderMode.Mesh)
                            {
                                Mesh[] meshes      = new Mesh[0];
                                int    highestPoly = 0;
                                renderer.GetMeshes(meshes);
                                if (meshes.Length == 0 && renderer.mesh != null)
                                {
                                    meshes = new Mesh[] { renderer.mesh };
                                }

                                // Debug.Log(meshes.Length + " meshes possible emmited meshes from " + ps.gameObject.name);
                                foreach (Mesh m in meshes)
                                {
                                    if (m.isReadable)
                                    {
                                        if (m.triangles.Length / 3 > highestPoly)
                                        {
                                            highestPoly = m.triangles.Length / 3;
                                        }
                                    }
                                    else
                                    {
                                        if (1000 > highestPoly)
                                        {
                                            highestPoly = int.MaxValue;
                                        }
                                    }
                                }

                                if (highestPoly > 0)
                                {
                                    highestPoly  = Mathf.Clamp(highestPoly / ps_mesh_particle_divider, 1, highestPoly);
                                    realtime_max = Mathf.FloorToInt((float)realtime_max / highestPoly);

                                    if (highestPoly > ps_mesh_particle_poly_limit)
                                    {
                                        Debug.LogError("Particle system named " + ps.gameObject.name + " breached polygon limits, it has been deleted");
                                        ValidationUtils.RemoveComponent(ps);
                                        continue;
                                    }
                                }
                            }
                        }


                        ParticleSystem.MinMaxCurve rate = emission.rateOverTime;

                        if (rate.mode == ParticleSystemCurveMode.Constant)
                        {
                            rate.constant = Mathf.Clamp(rate.constant, 0, ps_max_emission);
                        }
                        else if (rate.mode == ParticleSystemCurveMode.TwoConstants)
                        {
                            rate.constantMax = Mathf.Clamp(rate.constantMax, 0, ps_max_emission);
                        }
                        else
                        {
                            rate.curveMultiplier = Mathf.Clamp(rate.curveMultiplier, 0, ps_max_emission);
                        }

                        emission.rateOverTime = rate;
                        rate = emission.rateOverDistance;

                        if (rate.mode == ParticleSystemCurveMode.Constant)
                        {
                            rate.constant = Mathf.Clamp(rate.constant, 0, ps_max_emission);
                        }
                        else if (rate.mode == ParticleSystemCurveMode.TwoConstants)
                        {
                            rate.constantMax = Mathf.Clamp(rate.constantMax, 0, ps_max_emission);
                        }
                        else
                        {
                            rate.curveMultiplier = Mathf.Clamp(rate.curveMultiplier, 0, ps_max_emission);
                        }

                        emission.rateOverDistance = rate;

                        //Disable collision with PlayerLocal layer
                        collision.collidesWith &= ~(1 << 10);
                    }
                }

                particleSystems.Add(ps, realtime_max);
            }

            EnforceRealtimeParticleSystemLimits(particleSystems, true, false);

            return(particleSystems);
        }
        public static void EnforceAvatarStationLimits(GameObject currentAvatar)
        {
            using (_enforceAvatarStationLimitsProfilerMarker.Auto())
            {
                int stationCount = 0;
                foreach (VRC.SDKBase.VRCStation station in currentAvatar.gameObject.GetComponentsInChildren <VRC.SDKBase.VRCStation>(true))
                {
                    if (station == null)
                    {
                        continue;
                    }

                    #if VRC_CLIENT
                    VRC_StationInternal stationInternal = station.transform.GetComponent <VRC_StationInternal>();
                    #endif
                    if (stationCount < MAX_STATIONS_PER_AVATAR)
                    {
                        #if VRC_CLIENT
                        bool markedForDestruction = false;
                        #endif
                        // keep this station, but limit it
                        if (station.disableStationExit)
                        {
                            Debug.LogError("[" + currentAvatar.name + "]==> Stations on avatars cannot disable station exit. Re-enabled.");
                            station.disableStationExit = false;
                        }

                        if (station.stationEnterPlayerLocation != null)
                        {
                            if (Vector3.Distance(station.stationEnterPlayerLocation.position, station.transform.position) > MAX_STATION_LOCATION_DISTANCE)
                            {
                                #if VRC_CLIENT
                                markedForDestruction = true;
                                Debug.LogError(
                                    "[" + currentAvatar.name + "]==> Station enter location is too far from station (max dist=" + MAX_STATION_LOCATION_DISTANCE +
                                    "). Station disabled.");
                                #else
                                Debug.LogError("Station enter location is too far from station (max dist=" + MAX_STATION_LOCATION_DISTANCE + "). Station will be disabled at runtime.");
                                #endif
                            }

                            if (Vector3.Distance(station.stationExitPlayerLocation.position, station.transform.position) > MAX_STATION_LOCATION_DISTANCE)
                            {
                                #if VRC_CLIENT
                                markedForDestruction = true;
                                Debug.LogError(
                                    "[" + currentAvatar.name + "]==> Station exit location is too far from station (max dist=" + MAX_STATION_LOCATION_DISTANCE +
                                    "). Station disabled.");
                                #else
                                Debug.LogError("Station exit location is too far from station (max dist=" + MAX_STATION_LOCATION_DISTANCE + "). Station will be disabled at runtime.");
                                #endif
                            }

                            #if VRC_CLIENT
                            if (markedForDestruction)
                            {
                                ValidationUtils.RemoveComponent(station);
                                if (stationInternal != null)
                                {
                                    ValidationUtils.RemoveComponent(stationInternal);
                                }
                            }
                            #endif
                        }
                    }
                    else
                    {
                        #if VRC_CLIENT
                        Debug.LogError("[" + currentAvatar.name + "]==> Removing station over limit of " + MAX_STATIONS_PER_AVATAR);
                        ValidationUtils.RemoveComponent(station);
                        if (stationInternal != null)
                        {
                            ValidationUtils.RemoveComponent(stationInternal);
                        }
                        #else
                        Debug.LogError("Too many stations on avatar(" + currentAvatar.name + "). Maximum allowed=" + MAX_STATIONS_PER_AVATAR + ". Extra stations will be removed at runtime.");
                        #endif
                    }

                    stationCount++;
                }
            }
        }
 public static IEnumerable <Component> FindIllegalComponents(GameObject target)
 {
     return(ValidationUtils.FindIllegalComponents(target, GetWhitelistForSDK(target)));
 }
        public static void EnforceAudioSourceLimits(GameObject currentAvatar)
        {
            using (_enforceAudioSourceLimitsProfilerMarker.Auto())
            {
                if (currentAvatar == null)
                {
                    return;
                }

                Queue <GameObject> children = new Queue <GameObject>();
                if (currentAvatar != null)
                {
                    children.Enqueue(currentAvatar.gameObject);
                }

                while (children.Count > 0)
                {
                    GameObject child = children.Dequeue();
                    if (child == null)
                    {
                        continue;
                    }

                    int childCount = child.transform.childCount;
                    for (int idx = 0; idx < childCount; ++idx)
                    {
                        children.Enqueue(child.transform.GetChild(idx).gameObject);
                    }

                    #if VRC_CLIENT
                    if (child.GetComponent <USpeaker>() != null)
                    {
                        continue;
                    }
                    #endif

                    AudioSource[] sources = child.transform.GetComponents <AudioSource>();
                    if (sources == null || sources.Length <= 0)
                    {
                        continue;
                    }

                    AudioSource audioSource = sources[0];
                    if (audioSource == null)
                    {
                        continue;
                    }


                    #if VRC_CLIENT
                    audioSource.outputAudioMixerGroup = VRCAudioManager.GetAvatarGroup();
                    audioSource.priority = Mathf.Clamp(audioSource.priority, 200, 255);
                    #else
                    ProcessSpatialAudioSources(audioSource);
                    #endif //!VRC_CLIENT

                    if (sources.Length <= 1)
                    {
                        continue;
                    }

                    Debug.LogError("Disabling extra AudioSources on GameObject(" + child.name + "). Only one is allowed per GameObject.");
                    for (int i = 1; i < sources.Length; i++)
                    {
                        if (sources[i] == null)
                        {
                            Profiler.EndSample();
                            continue;
                        }

                        #if VRC_CLIENT
                        sources[i].enabled = false;
                        sources[i].clip    = null;
                        #else
                        ValidationUtils.RemoveComponent(sources[i]);
                        #endif //!VRC_CLIENT
                    }
                }
            }
        }
 public static void RemoveIllegalComponents(GameObject target, bool retry = true)
 {
     ValidationUtils.RemoveIllegalComponents(target, GetWhitelistForSDK(target), retry);
 }