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); }