public static void RemoveCameras(GameObject currentAvatar, bool localPlayer, bool friend)
        {
            if (!localPlayer && currentAvatar != null)
            {
                foreach (Camera camera in currentAvatar.GetComponentsInChildren <Camera>(true))
                {
                    if (camera == null || camera.gameObject == null)
                    {
                        continue;
                    }

                    Debug.LogWarning("Removing camera from " + camera.gameObject.name);

                    if (friend && camera.targetTexture != null)
                    {
                        camera.enabled = false;
                    }
                    else
                    {
                        camera.enabled = false;
                        if (camera.targetTexture != null)
                        {
                            camera.targetTexture = new RenderTexture(16, 16, 24);
                        }
                        ValidationUtils.RemoveComponent(camera);
                    }
                }
            }
        }
        public static void RemoveExtraAnimationComponents(GameObject currentAvatar)
        {
            if (currentAvatar == null)
            {
                return;
            }

            // remove Animator comps
            {
                Animator mainAnimator       = currentAvatar.GetComponent <Animator>();
                bool     removeMainAnimator = false;
                if (mainAnimator != null)
                {
                    if (!mainAnimator.isHuman || mainAnimator.avatar == null || !mainAnimator.avatar.isValid)
                    {
                        removeMainAnimator = true;
                    }
                }

                foreach (Animator anim in currentAvatar.GetComponentsInChildren <Animator>(true))
                {
                    if (anim == null || anim.gameObject == null)
                    {
                        continue;
                    }

                    // exclude the main avatar animator
                    if (anim == mainAnimator)
                    {
                        if (!removeMainAnimator)
                        {
                            continue;
                        }
                    }

                    Debug.LogWarning("Removing Animator comp from " + anim.gameObject.name);

                    anim.enabled = false;
                    ValidationUtils.RemoveComponent(anim);
                }
            }

            ValidationUtils.RemoveComponentsOfType <Animation>(currentAvatar);
        }
        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 IEnumerator EnforceAvatarStationLimitsEnumerator(GameObject currentAvatar, System.Action <VRC_Station> onFound)
        {
            Queue <GameObject> children = new Queue <GameObject>();

            children.Enqueue(currentAvatar.gameObject);

            int  stationCount = 0;
            uint objectsProcessedThisFrame = 0;

            while (children.Count > 0)
            {
                if (Time.frameCount > _enforceAvatarStationsFrameNumber)
                {
                    _enforceAvatarStationsFrameNumber        = Time.frameCount;
                    _enforceAvatarStationsProcessedThisFrame = 0;
                }

                if (_enforceAvatarStationsProcessedThisFrame > ENFORCE_STATIONS_GAMEOBJECTS_PER_FRAME)
                {
                    yield return(null);
                }

                Profiler.BeginSample("EnforceAvatarStationLimitsEnumerator");
                _enforceAvatarStationsProcessedThisFrame++;

                GameObject child = children.Dequeue();
                if (child == null)
                {
                    Profiler.EndSample();
                    continue;
                }

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

                VRC_Station[] stations = child.transform.GetComponents <VRC_Station>();
                if (stations != null && stations.Length > 0)
                {
                    foreach (VRC_Station station in stations)
                    {
                        if (station == null)
                        {
                            Profiler.EndSample();
                            continue;
                        }

                        #if VRC_CLIENT
                        VRC_StationInternal stationInternal = station.transform.GetComponent <VRC_StationInternal>();
                        #endif
                        if (stationCount < MAX_STATIONS_PER_AVATAR)
                        {
                            bool markedForDestruction = false;
                            // 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 (markedForDestruction)
                                {
                                    #if VRC_CLIENT
                                    ValidationUtils.RemoveComponent(station);
                                    if (stationInternal != null)
                                    {
                                        ValidationUtils.RemoveComponent(stationInternal);
                                    }
                                    #endif
                                }
                                else
                                {
                                    if (onFound != null)
                                    {
                                        onFound(station);
                                    }
                                }
                            }
                        }
                        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++;
                    }
                }
                Profiler.EndSample();

                if (objectsProcessedThisFrame < ENFORCE_STATIONS_GAMEOBJECTS_PER_FRAME)
                {
                    continue;
                }

                objectsProcessedThisFrame = 0;
                yield return(null);
            }
        }
        public static IEnumerator EnforceAudioSourceLimitsEnumerator(GameObject currentAvatar, System.Action <AudioSource> onFound)
        {
            if (currentAvatar == null)
            {
                yield break;
            }

            Queue <GameObject> children = new Queue <GameObject>();

            if (currentAvatar != null)
            {
                children.Enqueue(currentAvatar.gameObject);
            }

            while (children.Count > 0)
            {
                if (Time.frameCount > _enforceAudioSourcesFrameNumber)
                {
                    _enforceAudioSourcesFrameNumber        = Time.frameCount;
                    _enforceAudioSourcesProcessedThisFrame = 0;
                }

                if (_enforceAudioSourcesProcessedThisFrame > ENFORCE_AUDIO_SOURCE_GAMEOBJECTS_PER_FRAME)
                {
                    yield return(null);
                }

                Profiler.BeginSample("EnforceAudioSourceLimitsEnumerator");
                _enforceAudioSourcesProcessedThisFrame++;

                GameObject child = children.Dequeue();
                if (child == null)
                {
                    Profiler.EndSample();
                    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)
                {
                    Profiler.EndSample();
                    continue;
                }
                #endif

                AudioSource[] sources = child.transform.GetComponents <AudioSource>();
                if (sources != null && sources.Length > 0)
                {
                    AudioSource audioSource = sources[0];
                    if (audioSource == null)
                    {
                        Profiler.EndSample();
                        continue;
                    }

                    VRC_SpatialAudioSource vrcSpatialAudioSource = audioSource.gameObject.GetComponent <VRC_SpatialAudioSource>();

                    #if VRC_CLIENT
                    audioSource.outputAudioMixerGroup = VRCAudioManager.GetAvatarGroup();
                    audioSource.priority = Mathf.Clamp(audioSource.priority, 200, 255);
                    if (vrcSpatialAudioSource != null)
                    {
                        // copy the values into the onsp component
                        var onspAudioSource = audioSource.gameObject.GetOrAddComponent <ONSPAudioSource>();
                        onspAudioSource.Gain                 = vrcSpatialAudioSource.Gain;
                        onspAudioSource.Near                 = vrcSpatialAudioSource.Near;
                        onspAudioSource.Far                  = vrcSpatialAudioSource.Far;
                        onspAudioSource.VolumetricRadius     = vrcSpatialAudioSource.VolumetricRadius;
                        onspAudioSource.EnableSpatialization = vrcSpatialAudioSource.EnableSpatialization;
                        onspAudioSource.UseInvSqr            = !vrcSpatialAudioSource.UseAudioSourceVolumeCurve;
                        if (!vrcSpatialAudioSource.EnableSpatialization)
                        {
                            audioSource.spatialize = false;
                        }
                    }
                    #else
                    // these are SDK only, we rely on AvatarAudioSourceLimiter to enforce
                    // values at runtime

                    #if SUPPORT_DEPRECATED_ONSP
                    ONSPAudioSource[] allOnsp = audioSource.gameObject.GetComponents <ONSPAudioSource>();
                    if (allOnsp != null && allOnsp.Length > 0)
                    {
                        ONSPAudioSource onsp = allOnsp[0];
                        if (vrcSpatialAudioSource == null)
                        {
                            vrcSpatialAudioSource = audioSource.gameObject.AddComponent <VRC_SpatialAudioSource>();
                        }

                        vrcSpatialAudioSource.Gain                      = Mathf.Min(onsp.Gain, VRCSDK2.AudioManagerSettings.AvatarAudioMaxGain);
                        vrcSpatialAudioSource.Far                       = Mathf.Min(onsp.Far, VRCSDK2.AudioManagerSettings.AvatarAudioMaxRange);
                        vrcSpatialAudioSource.VolumetricRadius          = Mathf.Min(onsp.Far, VRCSDK2.AudioManagerSettings.AvatarAudioMaxRange);
                        vrcSpatialAudioSource.Near                      = Mathf.Min(onsp.Near, onsp.Far);
                        vrcSpatialAudioSource.EnableSpatialization      = onsp.EnableSpatialization;
                        vrcSpatialAudioSource.UseAudioSourceVolumeCurve = !onsp.UseInvSqr;

                        Debug.LogWarningFormat("ONSPAudioSource found on {0}. converted to VRC_SpatialAudioSource.", child.name);
                        foreach (var o in allOnsp)
                        {
                            Object.DestroyImmediate(o, true);
                        }
                    }
                    #endif
                    if (vrcSpatialAudioSource == null)
                    {
                        // user has not yet added VRC_SpatialAudioSource (or ONSP)
                        // so set up some defaults
                        vrcSpatialAudioSource                      = audioSource.gameObject.AddComponent <VRC_SpatialAudioSource>();
                        vrcSpatialAudioSource.Gain                 = AudioManagerSettings.AvatarAudioMaxGain;
                        vrcSpatialAudioSource.Far                  = AudioManagerSettings.AvatarAudioMaxRange;
                        vrcSpatialAudioSource.Near                 = 0f;
                        vrcSpatialAudioSource.VolumetricRadius     = 0f;
                        vrcSpatialAudioSource.EnableSpatialization = true;
                        vrcSpatialAudioSource.enabled              = true;
                        audioSource.spatialize                     = true;
                        audioSource.priority              = Mathf.Clamp(audioSource.priority, 200, 255);
                        audioSource.bypassEffects         = false;
                        audioSource.bypassListenerEffects = false;
                        audioSource.spatialBlend          = 1f;
                        audioSource.spread = 0;

                        // user is allowed to change, but for now put a safe default
                        audioSource.maxDistance = AudioManagerSettings.AvatarAudioMaxRange;
                        audioSource.minDistance = audioSource.maxDistance / 500f;
                        audioSource.rolloffMode = AudioRolloffMode.Logarithmic;
                    }
                    #endif //!VRC_CLIENT

                    onFound(audioSource);

                    if (sources.Length > 1)
                    {
                        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
                        }
                    }
                }

                Profiler.EndSample();
            }
        }
Example #6
0
        private static IEnumerator EnforceAudioSourceLimitsEnumerator(GameObject currentAvatar, System.Action <AudioSource> onFound)
        {
            if (currentAvatar == null)
            {
                yield break;
            }

            Queue <GameObject> children = new Queue <GameObject>();

            if (currentAvatar != null)
            {
                children.Enqueue(currentAvatar.gameObject);
            }

            while (children.Count > 0)
            {
                if (Time.frameCount > _enforceAudioSourcesFrameNumber)
                {
                    _enforceAudioSourcesFrameNumber        = Time.frameCount;
                    _enforceAudioSourcesProcessedThisFrame = 0;
                }

                if (_enforceAudioSourcesProcessedThisFrame > ENFORCE_AUDIO_SOURCE_GAMEOBJECTS_PER_FRAME)
                {
                    yield return(null);
                }

                Profiler.BeginSample("EnforceAudioSourceLimitsEnumerator");
                _enforceAudioSourcesProcessedThisFrame++;

                GameObject child = children.Dequeue();
                if (child == null)
                {
                    Profiler.EndSample();
                    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)
                {
                    Profiler.EndSample();
                    continue;
                }
#endif

                AudioSource[] sources = child.transform.GetComponents <AudioSource>();
                if (sources != null && sources.Length > 0)
                {
                    AudioSource audioSource = sources[0];
                    if (audioSource == null)
                    {
                        Profiler.EndSample();
                        continue;
                    }


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

                    onFound(audioSource);

                    if (sources.Length > 1)
                    {
                        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
                        }
                    }
                }

                Profiler.EndSample();
            }
        }
        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 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
                    }
                }
            }
        }