Decompiled source of ValheimBuildOptimization v0.3.0

plugins/ValheimBuildOptimization.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ValheimBuildOptimization")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimBuildOptimization")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("be15940e-381c-4623-a8d4-222869a01afc")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace BuildPieceProfiler;

[BepInPlugin("valheim.buildpieceprofiler", "Build Piece Profiler", "0.2.0")]
public class BuildPieceProfilerPlugin : BaseUnityPlugin
{
	private enum AppliedFireMode
	{
		None,
		StaticLight,
		FullCull
	}

	private class FireCandidate
	{
		public Piece Piece;

		public MeshRenderer[] Renderers;

		public Light[] OriginalLights;

		public ParticleSystem[] Particles;
	}

	private class FireOptimizationState
	{
		public Piece Piece;

		public AppliedFireMode AppliedMode = AppliedFireMode.None;

		public readonly Dictionary<Light, bool> OriginalLightEnabled = new Dictionary<Light, bool>();

		public readonly Dictionary<ParticleSystem, bool> OriginalParticlePlaying = new Dictionary<ParticleSystem, bool>();

		public readonly Dictionary<MeshRenderer, ShadowCastingMode> OriginalShadowModes = new Dictionary<MeshRenderer, ShadowCastingMode>();

		public GameObject ProxyLightObject;

		public Light ProxyLight;

		public float LastVisibleTime;

		public float LastIrrelevantTime;
	}

	private enum FireCullingMode
	{
		Off,
		StaticLight,
		FullCull
	}

	private struct FireMetrics
	{
		public int FireCandidates;

		public int RendererVisibleFireCandidates;

		public int OccludedFireCandidates;

		public int RelevantFireCandidates;

		public int HiddenOrIrrelevantFireCandidates;

		public int OptimizedFirePieces;

		public int StaticLightFirePieces;

		public int FullCullFirePieces;

		public int FireProxyLightsActive;

		public int FireOriginalLightsDisabled;

		public int FireParticlesStopped;

		public int FireShadowsDisabled;
	}

	private struct Counts
	{
		public int Pieces;

		public int WearNTear;

		public int ZNetView;

		public int MeshRenderer;

		public int EnabledMeshRenderer;

		public int VisibleMeshRenderer;

		public int Collider;

		public int EnabledCollider;

		public int LODGroup;

		public int Light;

		public int EnabledLight;

		public int ParticleSystem;

		public int ActiveParticleSystem;

		public int AudioSource;

		public int ActiveRigidbody;

		public int PieceMeshRenderer;

		public int PieceEnabledMeshRenderer;

		public int PieceVisibleMeshRenderer;

		public int PieceCollider;

		public int PieceEnabledCollider;

		public int PieceLODGroup;

		public int PieceLight;

		public int PieceEnabledLight;

		public int PieceParticleSystem;

		public int PieceActiveParticleSystem;

		public int PieceAudioSource;

		public int PieceRigidbody;

		public int PieceActiveRigidbody;

		public int PiecesWithinNear;

		public int PiecesWithinMedium;

		public int PiecesWithinFar;

		public int PiecesWithinVeryFar;

		public int PiecesWithinExtreme;

		public int PiecesBeyondExtreme;

		public float NearestPieceDistance;

		public float FarthestPieceDistance;

		public int FireCandidates;

		public int RendererVisibleFireCandidates;

		public int OccludedFireCandidates;

		public int RelevantFireCandidates;

		public int HiddenOrIrrelevantFireCandidates;

		public int OptimizedFirePieces;

		public int StaticLightFirePieces;

		public int FullCullFirePieces;

		public int FireProxyLightsActive;

		public int FireOriginalLightsDisabled;

		public int FireParticlesStopped;

		public int FireShadowsDisabled;
	}

	public const string PluginGuid = "valheim.buildpieceprofiler";

	public const string PluginName = "Build Piece Profiler";

	public const string PluginVersion = "0.2.0";

	private const string StaticFireLightProxyName = "BuildPieceProfiler_StaticFireLightProxy";

	private readonly Rect _windowRectDefault = new Rect(20f, 40f, 520f, 980f);

	private Rect _windowRect;

	private Vector2 _scrollPosition = Vector2.zero;

	private bool _showOverlay;

	private float _nextPollTime;

	private Counts _counts = default(Counts);

	private ConfigEntry<bool> _enableProfiler;

	private ConfigEntry<bool> _enableConsoleLogging;

	private ConfigEntry<bool> _showProfilerOnStart;

	private ConfigEntry<float> _profilerPollInterval;

	private ConfigEntry<KeyCode> _toggleProfilerKey;

	private ConfigEntry<bool> _enableOptimizations;

	private ConfigEntry<float> _nearDistance;

	private ConfigEntry<float> _mediumDistance;

	private ConfigEntry<float> _farDistance;

	private ConfigEntry<float> _veryFarDistance;

	private ConfigEntry<float> _extremeDistance;

	private readonly Dictionary<Piece, FireOptimizationState> _fireStates = new Dictionary<Piece, FireOptimizationState>();

	private readonly List<FireOptimizationState> _activeFireStates = new List<FireOptimizationState>();

	private readonly List<FireCandidate> _fireCandidates = new List<FireCandidate>();

	private FireMetrics _fireMetrics = default(FireMetrics);

	private float _nextOptimizerUpdateTime;

	private float _nextFireCandidateRefreshTime;

	private bool _optimizationsWereActive;

	private ConfigEntry<float> _optimizerUpdateInterval;

	private ConfigEntry<float> _fireCandidateRefreshInterval;

	private ConfigEntry<FireCullingMode> _fireCullingMode;

	private ConfigEntry<bool> _useFireVisibilityCulling;

	private ConfigEntry<float> _fireVisibilityGraceSeconds;

	private ConfigEntry<float> _neverOptimizeFireWithin;

	private ConfigEntry<float> _minimumFullCullDistance;

	private ConfigEntry<float> _fireDistanceCullStart;

	private ConfigEntry<float> _fireDistanceRestore;

	private ConfigEntry<float> _staticLightIntensityMultiplier;

	private ConfigEntry<float> _staticLightRangeMultiplier;

	private ConfigEntry<bool> _useFireOcclusionCulling;

	private ConfigEntry<float> _fireOcclusionRayRadius;

	private ConfigEntry<bool> _debugFireOcclusion;

	private ConfigEntry<float> _staticLightProxyMaxDistance;

	private ConfigEntry<float> _staticLightOccludedProxyMaxDistance;

	private ConfigEntry<float> _fireRestoreGraceSeconds;

	private Texture2D _opaqueBackground;

	private bool IsFireCandidate(Light[] lights, ParticleSystem[] particles)
	{
		if (lights == null || particles == null || lights.Length == 0 || particles.Length == 0)
		{
			return false;
		}
		bool flag = false;
		bool flag2 = false;
		foreach (Light val in lights)
		{
			if ((Object)(object)val != (Object)null)
			{
				flag = true;
				break;
			}
		}
		foreach (ParticleSystem val2 in particles)
		{
			if ((Object)(object)val2 != (Object)null)
			{
				flag2 = true;
				break;
			}
		}
		return flag && flag2;
	}

	private bool IsPieceVisible(MeshRenderer[] renderers)
	{
		if (renderers == null)
		{
			return false;
		}
		foreach (MeshRenderer val in renderers)
		{
			if (!((Object)(object)val == (Object)null) && ((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy && ((Renderer)val).isVisible)
			{
				return true;
			}
		}
		return false;
	}

	private void LateUpdate()
	{
		if (_enableOptimizations.Value && _fireCullingMode.Value != 0)
		{
			EnforceOptimizedFireLightsOnly();
		}
	}

	private void EnforceOptimizedFireLightsOnly()
	{
		for (int num = _activeFireStates.Count - 1; num >= 0; num--)
		{
			FireOptimizationState fireOptimizationState = _activeFireStates[num];
			if (fireOptimizationState == null || fireOptimizationState.AppliedMode == AppliedFireMode.None)
			{
				_activeFireStates.RemoveAt(num);
			}
			else
			{
				foreach (KeyValuePair<Light, bool> item in fireOptimizationState.OriginalLightEnabled)
				{
					Light key = item.Key;
					if (!((Object)(object)key == (Object)null) && !IsOurProxyLight(key) && ((Behaviour)key).enabled)
					{
						((Behaviour)key).enabled = false;
					}
				}
				if (fireOptimizationState.AppliedMode == AppliedFireMode.FullCull)
				{
					DisableProxyLight(fireOptimizationState);
				}
			}
		}
	}

	private void RestoreFire(Piece piece)
	{
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)piece == (Object)null || !_fireStates.TryGetValue(piece, out var value))
		{
			return;
		}
		foreach (KeyValuePair<Light, bool> item in value.OriginalLightEnabled)
		{
			if ((Object)(object)item.Key != (Object)null)
			{
				((Behaviour)item.Key).enabled = item.Value;
			}
		}
		foreach (KeyValuePair<MeshRenderer, ShadowCastingMode> originalShadowMode in value.OriginalShadowModes)
		{
			if ((Object)(object)originalShadowMode.Key != (Object)null)
			{
				((Renderer)originalShadowMode.Key).shadowCastingMode = originalShadowMode.Value;
			}
		}
		foreach (KeyValuePair<ParticleSystem, bool> item2 in value.OriginalParticlePlaying)
		{
			ParticleSystem key = item2.Key;
			if (!((Object)(object)key == (Object)null))
			{
				if (item2.Value)
				{
					key.Play(true);
				}
				else
				{
					key.Stop(true, (ParticleSystemStopBehavior)0);
				}
			}
		}
		DisableProxyLight(value);
		UntrackActiveFireState(value);
		value.AppliedMode = AppliedFireMode.None;
		value.OriginalLightEnabled.Clear();
		value.OriginalShadowModes.Clear();
		value.OriginalParticlePlaying.Clear();
	}

	private void RestoreAllFireOptimizations()
	{
		List<Piece> list = new List<Piece>(_fireStates.Keys);
		foreach (Piece item in list)
		{
			RestoreFire(item);
		}
		DestroyAllProxyLights();
	}

	private bool HasActiveFireOptimizations()
	{
		for (int num = _activeFireStates.Count - 1; num >= 0; num--)
		{
			FireOptimizationState fireOptimizationState = _activeFireStates[num];
			if (fireOptimizationState != null && fireOptimizationState.AppliedMode != 0)
			{
				return true;
			}
			_activeFireStates.RemoveAt(num);
		}
		return false;
	}

	private void TrackActiveFireState(FireOptimizationState state)
	{
		if (state != null && state.AppliedMode != 0 && !_activeFireStates.Contains(state))
		{
			_activeFireStates.Add(state);
		}
	}

	private void UntrackActiveFireState(FireOptimizationState state)
	{
		if (state != null)
		{
			_activeFireStates.Remove(state);
		}
	}

	private void ResetFireCandidateCache()
	{
		_fireCandidates.Clear();
		_nextFireCandidateRefreshTime = 0f;
		_fireMetrics = default(FireMetrics);
	}

	private void CleanupDestroyedFireStates()
	{
		List<KeyValuePair<Piece, FireOptimizationState>> list = null;
		foreach (KeyValuePair<Piece, FireOptimizationState> fireState in _fireStates)
		{
			Piece key = fireState.Key;
			if (!((Object)(object)key != (Object)null))
			{
				if (list == null)
				{
					list = new List<KeyValuePair<Piece, FireOptimizationState>>();
				}
				list.Add(fireState);
			}
		}
		if (list == null)
		{
			return;
		}
		foreach (KeyValuePair<Piece, FireOptimizationState> item in list)
		{
			UntrackActiveFireState(item.Value);
			_fireStates.Remove(item.Key);
		}
	}

	private void RefreshFireCandidateCache()
	{
		_fireCandidates.Clear();
		Piece[] array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
		HashSet<Piece> hashSet = new HashSet<Piece>();
		Piece[] array2 = array;
		foreach (Piece val in array2)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Light[] originalLights = GetOriginalLights(((Component)val).GetComponentsInChildren<Light>(true));
			if (originalLights.Length != 0)
			{
				ParticleSystem[] componentsInChildren = ((Component)val).GetComponentsInChildren<ParticleSystem>(true);
				if (IsFireCandidate(originalLights, componentsInChildren))
				{
					_fireCandidates.Add(new FireCandidate
					{
						Piece = val,
						Renderers = ((Component)val).GetComponentsInChildren<MeshRenderer>(true),
						OriginalLights = originalLights,
						Particles = componentsInChildren
					});
					hashSet.Add(val);
				}
			}
		}
		List<Piece> list = null;
		foreach (KeyValuePair<Piece, FireOptimizationState> fireState in _fireStates)
		{
			Piece key = fireState.Key;
			FireOptimizationState value = fireState.Value;
			if (!((Object)(object)key == (Object)null) && value != null && value.AppliedMode != 0 && !hashSet.Contains(key))
			{
				if (list == null)
				{
					list = new List<Piece>();
				}
				list.Add(key);
			}
		}
		if (list != null)
		{
			foreach (Piece item in list)
			{
				RestoreFire(item);
			}
		}
		_nextFireCandidateRefreshTime = Time.time + Mathf.Max(1f, _fireCandidateRefreshInterval.Value);
	}

	private void OnDestroy()
	{
		RestoreAllFireOptimizations();
	}

	private void ApplyStaticLight(Piece piece, FireOptimizationState state, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles, bool useProxyLight, bool stopParticles)
	{
		if (state.AppliedMode != AppliedFireMode.StaticLight)
		{
			RestoreFire(piece);
			state = new FireOptimizationState
			{
				Piece = piece,
				LastVisibleTime = Time.time,
				LastIrrelevantTime = Time.time
			};
			Light[] lights2 = (Light[])(((object)lights) ?? ((object)new Light[0]));
			StoreOriginalStates(state, renderers, lights2, particles);
			state.AppliedMode = AppliedFireMode.StaticLight;
			_fireStates[piece] = state;
		}
		TrackActiveFireState(state);
		Light[] lights3 = (Light[])(((object)lights) ?? ((object)new Light[0]));
		StoreOriginalStates(state, renderers, lights3, particles);
		if (useProxyLight)
		{
			Light firstUsableLight = GetFirstUsableLight(lights3);
			if ((Object)(object)firstUsableLight != (Object)null)
			{
				EnsureProxyLight(piece, state, firstUsableLight);
			}
			else
			{
				DisableProxyLight(state);
			}
		}
		else
		{
			DisableProxyLight(state);
		}
		DisableOriginalLights(lights3);
		if (stopParticles)
		{
			StopParticles(particles);
		}
		else
		{
			RestoreParticles(state, particles);
		}
		DisableShadows(renderers);
	}

	private void ApplyFullCull(Piece piece, FireOptimizationState state, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		if (state.AppliedMode != AppliedFireMode.FullCull)
		{
			RestoreFire(piece);
			state = GetOrCreateFireState(piece);
			Light[] lights2 = (Light[])(((object)lights) ?? ((object)new Light[0]));
			StoreOriginalStates(state, renderers, lights2, particles);
			state.AppliedMode = AppliedFireMode.FullCull;
		}
		TrackActiveFireState(state);
		Light[] lights3 = (Light[])(((object)lights) ?? ((object)new Light[0]));
		StoreOriginalStates(state, renderers, lights3, particles);
		DisableProxyLight(state);
		DisableOriginalLights(lights3);
		StopParticles(particles);
		DisableShadows(renderers);
	}

	private void StoreOriginalStates(FireOptimizationState state, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		foreach (Light val in lights)
		{
			if (!((Object)(object)val == (Object)null) && !state.OriginalLightEnabled.ContainsKey(val))
			{
				state.OriginalLightEnabled[val] = ((Behaviour)val).enabled;
			}
		}
		foreach (ParticleSystem val2 in particles)
		{
			if (!((Object)(object)val2 == (Object)null) && !state.OriginalParticlePlaying.ContainsKey(val2))
			{
				state.OriginalParticlePlaying[val2] = val2.IsAlive(true);
			}
		}
		foreach (MeshRenderer val3 in renderers)
		{
			if (!((Object)(object)val3 == (Object)null) && !state.OriginalShadowModes.ContainsKey(val3))
			{
				state.OriginalShadowModes[val3] = ((Renderer)val3).shadowCastingMode;
			}
		}
	}

	private void DisableOriginalLights(Light[] lights)
	{
		foreach (Light val in lights)
		{
			if (!((Object)(object)val == (Object)null))
			{
				((Behaviour)val).enabled = false;
			}
		}
	}

	private void DestroyAllProxyLights()
	{
		Piece[] array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
		foreach (Piece val in array)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Transform[] componentsInChildren = ((Component)val).GetComponentsInChildren<Transform>(true);
			foreach (Transform val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null && ((Object)((Component)val2).gameObject).name == "BuildPieceProfiler_StaticFireLightProxy")
				{
					Object.Destroy((Object)(object)((Component)val2).gameObject);
				}
			}
		}
	}

	private void RestoreParticles(FireOptimizationState state, ParticleSystem[] particles)
	{
		if (state == null || particles == null)
		{
			return;
		}
		foreach (ParticleSystem val in particles)
		{
			if (!((Object)(object)val == (Object)null) && state.OriginalParticlePlaying.TryGetValue(val, out var value) && value && ((Component)val).gameObject.activeInHierarchy && !val.isPlaying)
			{
				val.Play(true);
			}
		}
	}

	private void StopParticles(ParticleSystem[] particles)
	{
		foreach (ParticleSystem val in particles)
		{
			if (!((Object)(object)val == (Object)null))
			{
				val.Stop(true, (ParticleSystemStopBehavior)0);
			}
		}
	}

	private void DisableShadows(MeshRenderer[] renderers)
	{
		foreach (MeshRenderer val in renderers)
		{
			if (!((Object)(object)val == (Object)null))
			{
				((Renderer)val).shadowCastingMode = (ShadowCastingMode)0;
			}
		}
	}

	private Light GetFirstUsableLight(Light[] lights)
	{
		foreach (Light val in lights)
		{
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return null;
	}

	private void EnsureProxyLight(Piece piece, FireOptimizationState state, Light sourceLight)
	{
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Expected O, but got Unknown
		if ((Object)(object)piece == (Object)null || (Object)(object)sourceLight == (Object)null || state == null)
		{
			return;
		}
		if ((Object)(object)state.ProxyLightObject == (Object)null)
		{
			Transform val = ((Component)piece).transform.Find("BuildPieceProfiler_StaticFireLightProxy");
			if ((Object)(object)val != (Object)null)
			{
				state.ProxyLightObject = ((Component)val).gameObject;
				state.ProxyLight = state.ProxyLightObject.GetComponent<Light>();
				if ((Object)(object)state.ProxyLight == (Object)null)
				{
					state.ProxyLight = state.ProxyLightObject.AddComponent<Light>();
				}
			}
			else
			{
				state.ProxyLightObject = new GameObject("BuildPieceProfiler_StaticFireLightProxy");
				state.ProxyLightObject.transform.SetParent(((Component)piece).transform, false);
				state.ProxyLight = state.ProxyLightObject.AddComponent<Light>();
			}
		}
		state.ProxyLightObject.transform.position = ((Component)sourceLight).transform.position;
		state.ProxyLightObject.transform.rotation = ((Component)sourceLight).transform.rotation;
		state.ProxyLightObject.SetActive(true);
		state.ProxyLight.type = (LightType)2;
		state.ProxyLight.color = sourceLight.color;
		state.ProxyLight.intensity = sourceLight.intensity * Mathf.Max(0f, _staticLightIntensityMultiplier.Value);
		state.ProxyLight.range = sourceLight.range * Mathf.Max(0f, _staticLightRangeMultiplier.Value);
		state.ProxyLight.shadows = (LightShadows)0;
		((Behaviour)state.ProxyLight).enabled = true;
	}

	private void DisableProxyLight(FireOptimizationState state)
	{
		if ((Object)(object)state.ProxyLight != (Object)null)
		{
			((Behaviour)state.ProxyLight).enabled = false;
		}
		if ((Object)(object)state.ProxyLightObject != (Object)null)
		{
			state.ProxyLightObject.SetActive(false);
		}
	}

	private FireOptimizationState GetOrCreateFireState(Piece piece)
	{
		if (_fireStates.TryGetValue(piece, out var value))
		{
			return value;
		}
		value = new FireOptimizationState
		{
			Piece = piece,
			LastVisibleTime = Time.time,
			LastIrrelevantTime = Time.time
		};
		_fireStates[piece] = value;
		return value;
	}

	private Vector3 GetFireTargetPosition(Piece piece, Light[] lights, MeshRenderer[] renderers)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		if (lights != null && lights.Length != 0)
		{
			Vector3 val = Vector3.zero;
			int num = 0;
			foreach (Light val2 in lights)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					val += ((Component)val2).transform.position;
					num++;
				}
			}
			if (num > 0)
			{
				return val / (float)num;
			}
		}
		if (renderers != null && renderers.Length != 0)
		{
			Bounds bounds = default(Bounds);
			((Bounds)(ref bounds))..ctor(((Component)piece).transform.position, Vector3.zero);
			bool flag = false;
			foreach (MeshRenderer val3 in renderers)
			{
				if (!((Object)(object)val3 == (Object)null))
				{
					if (!flag)
					{
						bounds = ((Renderer)val3).bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref bounds)).Encapsulate(((Renderer)val3).bounds);
					}
				}
			}
			if (flag)
			{
				return ((Bounds)(ref bounds)).center;
			}
		}
		return ((Component)piece).transform.position;
	}

	private bool IsOurProxyLight(Light light)
	{
		if ((Object)(object)light == (Object)null)
		{
			return false;
		}
		if ((Object)(object)((Component)light).gameObject == (Object)null)
		{
			return false;
		}
		return ((Object)((Component)light).gameObject).name == "BuildPieceProfiler_StaticFireLightProxy";
	}

	private Light[] GetOriginalLights(Light[] lights)
	{
		if (lights == null || lights.Length == 0)
		{
			return (Light[])(object)new Light[0];
		}
		List<Light> list = new List<Light>();
		foreach (Light val in lights)
		{
			if (!((Object)(object)val == (Object)null) && !IsOurProxyLight(val))
			{
				list.Add(val);
			}
		}
		return list.ToArray();
	}

	private bool HitBelongsToPiece(RaycastHit hit, Piece piece)
	{
		if ((Object)(object)piece == (Object)null || (Object)(object)((RaycastHit)(ref hit)).collider == (Object)null)
		{
			return false;
		}
		Transform val = ((Component)((RaycastHit)(ref hit)).collider).transform;
		while ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)val == (Object)(object)((Component)piece).transform)
			{
				return true;
			}
			val = val.parent;
		}
		return false;
	}

	private bool IsFireOccludedFromCamera(Piece piece, Light[] lights, MeshRenderer[] renderers, Camera mainCamera)
	{
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		if (!_useFireOcclusionCulling.Value)
		{
			return false;
		}
		if ((Object)(object)piece == (Object)null || (Object)(object)mainCamera == (Object)null)
		{
			return false;
		}
		Vector3 position = ((Component)mainCamera).transform.position;
		Vector3 fireTargetPosition = GetFireTargetPosition(piece, lights, renderers);
		Vector3 val = fireTargetPosition - position;
		float magnitude = ((Vector3)(ref val)).magnitude;
		if (magnitude <= 0.1f)
		{
			return false;
		}
		val /= magnitude;
		float num = Mathf.Max(0f, _fireOcclusionRayRadius.Value);
		RaycastHit hit = default(RaycastHit);
		bool flag = ((!(num > 0f)) ? Physics.Raycast(position, val, ref hit, magnitude, -5, (QueryTriggerInteraction)1) : Physics.SphereCast(position, num, val, ref hit, magnitude, -5, (QueryTriggerInteraction)1));
		if (_debugFireOcclusion.Value)
		{
			Debug.DrawLine(position, fireTargetPosition, flag ? Color.red : Color.green, (_optimizerUpdateInterval != null) ? Mathf.Max(0.1f, _optimizerUpdateInterval.Value) : 0.5f);
		}
		if (!flag)
		{
			return false;
		}
		if (HitBelongsToPiece(hit, piece))
		{
			return false;
		}
		return true;
	}

	private bool ShouldUseStaticProxyLight(float distance, bool relevant)
	{
		if (relevant)
		{
			return distance <= Mathf.Max(0f, _staticLightProxyMaxDistance.Value);
		}
		return distance <= Mathf.Max(0f, _staticLightOccludedProxyMaxDistance.Value);
	}

	private void AddFireOptimizationStateMetrics(ref FireMetrics metrics)
	{
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Invalid comparison between Unknown and I4
		foreach (FireOptimizationState value in _fireStates.Values)
		{
			if (value == null || value.AppliedMode == AppliedFireMode.None)
			{
				continue;
			}
			metrics.OptimizedFirePieces++;
			if (value.AppliedMode == AppliedFireMode.StaticLight)
			{
				metrics.StaticLightFirePieces++;
			}
			if (value.AppliedMode == AppliedFireMode.FullCull)
			{
				metrics.FullCullFirePieces++;
			}
			if ((Object)(object)value.ProxyLight != (Object)null && ((Behaviour)value.ProxyLight).enabled && (Object)(object)value.ProxyLightObject != (Object)null && value.ProxyLightObject.activeInHierarchy)
			{
				metrics.FireProxyLightsActive++;
			}
			foreach (KeyValuePair<Light, bool> item in value.OriginalLightEnabled)
			{
				if ((Object)(object)item.Key != (Object)null && !IsOurProxyLight(item.Key) && !((Behaviour)item.Key).enabled && item.Value)
				{
					metrics.FireOriginalLightsDisabled++;
				}
			}
			foreach (KeyValuePair<ParticleSystem, bool> item2 in value.OriginalParticlePlaying)
			{
				if ((Object)(object)item2.Key != (Object)null && item2.Value && !item2.Key.IsAlive(true))
				{
					metrics.FireParticlesStopped++;
				}
			}
			foreach (KeyValuePair<MeshRenderer, ShadowCastingMode> originalShadowMode in value.OriginalShadowModes)
			{
				if ((Object)(object)originalShadowMode.Key != (Object)null && (int)((Renderer)originalShadowMode.Key).shadowCastingMode == 0 && (int)originalShadowMode.Value > 0)
				{
					metrics.FireShadowsDisabled++;
				}
			}
		}
	}

	private void UpdateFireOptimizations()
	{
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_0207: Unknown result type (might be due to invalid IL or missing references)
		//IL_020f: Unknown result type (might be due to invalid IL or missing references)
		if (_fireCullingMode.Value == FireCullingMode.Off)
		{
			if (HasActiveFireOptimizations())
			{
				RestoreAllFireOptimizations();
			}
			ResetFireCandidateCache();
			return;
		}
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			if (HasActiveFireOptimizations())
			{
				RestoreAllFireOptimizations();
			}
			ResetFireCandidateCache();
			return;
		}
		Vector3 position = ((Component)Player.m_localPlayer).transform.position;
		Camera main = Camera.main;
		if (Time.time >= _nextFireCandidateRefreshTime || _fireCandidates.Count == 0)
		{
			RefreshFireCandidateCache();
		}
		FireMetrics metrics = default(FireMetrics);
		for (int num = _fireCandidates.Count - 1; num >= 0; num--)
		{
			FireCandidate fireCandidate = _fireCandidates[num];
			if (fireCandidate == null || (Object)(object)fireCandidate.Piece == (Object)null)
			{
				_fireCandidates.RemoveAt(num);
				continue;
			}
			Piece piece = fireCandidate.Piece;
			if ((Object)(object)piece == (Object)null)
			{
				_fireCandidates.RemoveAt(num);
				continue;
			}
			MeshRenderer[] renderers = fireCandidate.Renderers;
			Light[] originalLights = fireCandidate.OriginalLights;
			ParticleSystem[] particles = fireCandidate.Particles;
			if (!IsFireCandidate(originalLights, particles))
			{
				RestoreFire(piece);
				_fireCandidates.RemoveAt(num);
				continue;
			}
			bool flag = IsPieceVisible(renderers);
			bool flag2 = flag && IsFireOccludedFromCamera(piece, originalLights, renderers, main);
			bool flag3 = flag && !flag2;
			metrics.FireCandidates++;
			if (flag)
			{
				metrics.RendererVisibleFireCandidates++;
			}
			if (flag2)
			{
				metrics.OccludedFireCandidates++;
			}
			if (flag3)
			{
				metrics.RelevantFireCandidates++;
			}
			else
			{
				metrics.HiddenOrIrrelevantFireCandidates++;
			}
			float num2 = Vector3.Distance(position, ((Component)piece).transform.position);
			FireOptimizationState orCreateFireState = GetOrCreateFireState(piece);
			if (flag3)
			{
				orCreateFireState.LastVisibleTime = Time.time;
			}
			else
			{
				orCreateFireState.LastIrrelevantTime = Time.time;
			}
			bool flag4 = !flag3;
			bool flag5 = _useFireVisibilityCulling.Value && flag4 && Time.time - orCreateFireState.LastVisibleTime >= Mathf.Max(0f, _fireVisibilityGraceSeconds.Value);
			bool flag6 = flag3 && Time.time - orCreateFireState.LastIrrelevantTime >= Mathf.Max(0f, _fireRestoreGraceSeconds.Value);
			bool flag7 = num2 >= Mathf.Max(1f, _fireDistanceCullStart.Value);
			bool flag8 = num2 <= Mathf.Max(1f, _fireDistanceRestore.Value);
			bool flag9 = num2 <= Mathf.Max(0f, _neverOptimizeFireWithin.Value);
			bool flag10 = !flag9 && (flag7 || flag5);
			bool flag11 = orCreateFireState.AppliedMode != AppliedFireMode.None;
			bool flag12 = flag6 && (flag8 || !flag7);
			if (flag9)
			{
				RestoreFire(piece);
				continue;
			}
			if (flag11)
			{
				if (flag12)
				{
					RestoreFire(piece);
					continue;
				}
			}
			else if (!flag10)
			{
				continue;
			}
			if (_fireCullingMode.Value == FireCullingMode.StaticLight)
			{
				bool useProxyLight = ShouldUseStaticProxyLight(num2, flag3);
				bool stopParticles = !flag3;
				ApplyStaticLight(piece, orCreateFireState, renderers, originalLights, particles, useProxyLight, stopParticles);
			}
			else if (_fireCullingMode.Value == FireCullingMode.FullCull)
			{
				float num3 = Mathf.Max(0f, _minimumFullCullDistance.Value);
				if (num2 >= num3 || flag7)
				{
					ApplyFullCull(piece, orCreateFireState, renderers, originalLights, particles);
					continue;
				}
				bool useProxyLight2 = ShouldUseStaticProxyLight(num2, flag3);
				bool stopParticles2 = !flag3;
				ApplyStaticLight(piece, orCreateFireState, renderers, originalLights, particles, useProxyLight2, stopParticles2);
			}
		}
		AddFireOptimizationStateMetrics(ref metrics);
		_fireMetrics = metrics;
		CleanupDestroyedFireStates();
	}

	private void Awake()
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_0422: Unknown result type (might be due to invalid IL or missing references)
		_windowRect = _windowRectDefault;
		_enableProfiler = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "EnableProfiler", true, "Enables the profiler system. If false, no polling or overlay drawing is performed.");
		_enableConsoleLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "EnableConsoleLogging", false, "Writes profiler counts to the BepInEx log each poll. Useful for testing, but can create large log files.");
		_showProfilerOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "ShowProfilerOnStart", true, "Shows the profiler overlay when the world loads.");
		_profilerPollInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Profiler", "PollIntervalSeconds", 1f, "How often the profiler updates its counts, in seconds. Lower values update faster but cost more performance.");
		_toggleProfilerKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Profiler", "ToggleProfilerKey", (KeyCode)288, "Keyboard key used to toggle the profiler overlay.");
		_enableOptimizations = ((BaseUnityPlugin)this).Config.Bind<bool>("Optimizations", "EnableOptimizations", false, "Master switch for optimization features. Currently reserved for future optimizer systems.");
		_nearDistance = ((BaseUnityPlugin)this).Config.Bind<float>("DistanceThresholds", "NearDistance", 10f, "Near distance in meters. Pieces within this range should usually remain fully vanilla.");
		_mediumDistance = ((BaseUnityPlugin)this).Config.Bind<float>("DistanceThresholds", "MediumDistance", 25f, "Medium distance in meters. Good candidate range for safe shadow/effect reductions.");
		_farDistance = ((BaseUnityPlugin)this).Config.Bind<float>("DistanceThresholds", "FarDistance", 50f, "Far distance in meters. Good candidate range for more aggressive visual optimizations.");
		_veryFarDistance = ((BaseUnityPlugin)this).Config.Bind<float>("DistanceThresholds", "VeryFarDistance", 100f, "Very far distance in meters. Future proxy/mesh-combining territory.");
		_extremeDistance = ((BaseUnityPlugin)this).Config.Bind<float>("DistanceThresholds", "ExtremeDistance", 200f, "Extreme distance in meters. Future impostor/proxy-only territory.");
		_optimizerUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Optimizations", "OptimizerUpdateIntervalSeconds", 0.5f, "How often the optimizer checks fire pieces, in seconds. Lower values react faster but cost more performance.");
		_fireCandidateRefreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Optimizations", "FireCandidateRefreshIntervalSeconds", 5f, "How often the optimizer does a full scene scan to discover fire pieces. Cached candidates are used between scans.");
		_fireRestoreGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "RestoreGraceSeconds", 1f, "How long a fire must remain relevant/visible before optimized fire effects are restored. Helps prevent flickering near occlusion edges.");
		_fireCullingMode = ((BaseUnityPlugin)this).Config.Bind<FireCullingMode>("FireCulling", "FireCullingMode", FireCullingMode.StaticLight, "Fire optimization mode. Off = no fire optimization, StaticLight = replace dynamic fire effects with a cheap static light, FullCull = disable fire visuals/effects at distance.");
		_useFireOcclusionCulling = ((BaseUnityPlugin)this).Config.Bind<bool>("FireCulling", "UseOcclusionCulling", true, "If true, fire candidates that are inside the camera view but blocked by solid geometry are treated as hidden/irrelevant.");
		_fireOcclusionRayRadius = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "OcclusionRayRadius", 0.05f, "Radius used for fire occlusion spherecasts. 0 uses a normal raycast. Small values like 0.05-0.15 can make occlusion detection more forgiving.");
		_debugFireOcclusion = ((BaseUnityPlugin)this).Config.Bind<bool>("FireCulling", "DebugFireOcclusion", false, "If true, draws debug rays for fire occlusion checks in the Unity scene view/logical debug view where supported.");
		_useFireVisibilityCulling = ((BaseUnityPlugin)this).Config.Bind<bool>("FireCulling", "UseVisibilityCulling", true, "If true, fire effects may be optimized when the fire is not visible for a short grace period.");
		_fireVisibilityGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "VisibilityGraceSeconds", 1.5f, "How long a fire must remain not visible before visibility-based optimization can activate.");
		_neverOptimizeFireWithin = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "NeverOptimizeFireWithin", 2f, "Fire pieces closer than this distance are never optimized.");
		_minimumFullCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "MinimumFullCullDistance", 15f, "Minimum distance required before FullCull mode may fully remove fire light/effects due to visibility.");
		_fireDistanceCullStart = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "FireDistanceCullStart", 50f, "Distance beyond which fire optimization activates regardless of visibility.");
		_fireDistanceRestore = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "FireDistanceRestore", 40f, "Distance within which fire optimization restores to normal. Should be lower than FireDistanceCullStart.");
		_staticLightIntensityMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "StaticLightIntensityMultiplier", 0.65f, "Multiplier for proxy static light intensity in StaticLight mode.");
		_staticLightRangeMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "StaticLightRangeMultiplier", 0.85f, "Multiplier for proxy static light range in StaticLight mode.");
		_staticLightProxyMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "StaticLightProxyMaxDistance", 50f, "Maximum distance at which StaticLight mode may create a proxy light for relevant/visible fires. Beyond this, optimized fires get particles/shadows removed but no replacement light.");
		_staticLightOccludedProxyMaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("FireCulling", "StaticLightOccludedProxyMaxDistance", 25f, "Maximum distance at which StaticLight mode may create a proxy light for hidden or occluded fires. Farther hidden fires receive no proxy light.");
		_showOverlay = _showProfilerOnStart.Value;
		_opaqueBackground = MakeTexture(2, 2, new Color(0.05f, 0.05f, 0.05f, 1f));
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Build Piece Profiler 0.2.0 loaded.");
	}

	private void Update()
	{
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		if (_enableProfiler.Value)
		{
			if (Input.GetKeyDown(_toggleProfilerKey.Value))
			{
				_showOverlay = !_showOverlay;
			}
			bool flag = _showOverlay || _enableConsoleLogging.Value;
			float num = Mathf.Max(0.1f, _profilerPollInterval.Value);
			if (flag && Time.time >= _nextPollTime)
			{
				_nextPollTime = Time.time + num;
				_counts = PollCounts();
				if (_enableConsoleLogging.Value)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)($"Pieces={_counts.Pieces}, " + $"WearNTear={_counts.WearNTear}, " + $"ZNetView={_counts.ZNetView}, " + $"MeshRenderer={_counts.MeshRenderer}, " + $"EnabledMeshRenderer={_counts.EnabledMeshRenderer}, " + $"VisibleMeshRenderer={_counts.VisibleMeshRenderer}, " + $"Collider={_counts.Collider}, " + $"EnabledCollider={_counts.EnabledCollider}, " + $"LODGroup={_counts.LODGroup}, " + $"Light={_counts.Light}, " + $"EnabledLight={_counts.EnabledLight}, " + $"ParticleSystem={_counts.ParticleSystem}, " + $"ActiveParticleSystem={_counts.ActiveParticleSystem}, " + $"AudioSource={_counts.AudioSource}, " + $"ActiveRigidbody={_counts.ActiveRigidbody}, " + $"PieceMeshRenderer={_counts.PieceMeshRenderer}, " + $"PieceEnabledMeshRenderer={_counts.PieceEnabledMeshRenderer}, " + $"PieceVisibleMeshRenderer={_counts.PieceVisibleMeshRenderer}, " + $"PieceCollider={_counts.PieceCollider}, " + $"PieceEnabledCollider={_counts.PieceEnabledCollider}"));
				}
			}
		}
		if (!_enableOptimizations.Value || _fireCullingMode.Value == FireCullingMode.Off)
		{
			if (_optimizationsWereActive || HasActiveFireOptimizations())
			{
				RestoreAllFireOptimizations();
				ResetFireCandidateCache();
			}
			_optimizationsWereActive = false;
			return;
		}
		if (!_optimizationsWereActive)
		{
			_nextOptimizerUpdateTime = 0f;
			_nextFireCandidateRefreshTime = 0f;
			_optimizationsWereActive = true;
		}
		float num2 = Mathf.Max(0.1f, _optimizerUpdateInterval.Value);
		if (Time.time >= _nextOptimizerUpdateTime)
		{
			_nextOptimizerUpdateTime = Time.time + num2;
			UpdateFireOptimizations();
		}
	}

	private void OnGUI()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		if (_enableProfiler.Value && _showOverlay)
		{
			_windowRect = GUILayout.Window(872391, _windowRect, new WindowFunction(DrawWindow), "Build Piece Profiler", (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.Width(520f),
				GUILayout.Height(940f)
			});
		}
	}

	private void DrawWindow(int windowId)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		GUI.DrawTexture(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height), (Texture)(object)_opaqueBackground, (ScaleMode)0);
		_scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[2]
		{
			GUILayout.Width(500f),
			GUILayout.Height(1050f)
		});
		GUILayout.Label("Toggle overlay: F7", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Poll interval: {_profilerPollInterval.Value:0.0}s", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Optimizations enabled: {_enableOptimizations.Value}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Global Scene Counts", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total Piece count: {_counts.Pieces}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total WearNTear count: {_counts.WearNTear}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total ZNetView count: {_counts.ZNetView}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Global Renderers", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total MeshRenderer count: {_counts.MeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Enabled MeshRenderer count: {_counts.EnabledMeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Visible MeshRenderer count: {_counts.VisibleMeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total LODGroup count: {_counts.LODGroup}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Global Physics", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total Collider count: {_counts.Collider}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Enabled Collider count: {_counts.EnabledCollider}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total active Rigidbody count: {_counts.ActiveRigidbody}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Global Effects", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total Light count: {_counts.Light}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Enabled Light count: {_counts.EnabledLight}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total ParticleSystem count: {_counts.ParticleSystem}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Active ParticleSystem count: {_counts.ActiveParticleSystem}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Total AudioSource count: {_counts.AudioSource}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(12f);
		GUILayout.Label("Build-Piece-Only Counts", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece MeshRenderer count: {_counts.PieceMeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece enabled MeshRenderer count: {_counts.PieceEnabledMeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece visible MeshRenderer count: {_counts.PieceVisibleMeshRenderer}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece LODGroup count: {_counts.PieceLODGroup}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Build-Piece Distance Buckets", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces within Near ({_nearDistance.Value:0}m): {_counts.PiecesWithinNear}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces within Medium ({_mediumDistance.Value:0}m): {_counts.PiecesWithinMedium}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces within Far ({_farDistance.Value:0}m): {_counts.PiecesWithinFar}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces within Very Far ({_veryFarDistance.Value:0}m): {_counts.PiecesWithinVeryFar}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces within Extreme ({_extremeDistance.Value:0}m): {_counts.PiecesWithinExtreme}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Pieces beyond Extreme: {_counts.PiecesBeyondExtreme}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Nearest piece distance: {_counts.NearestPieceDistance:0.0}m", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Farthest piece distance: {_counts.FarthestPieceDistance:0.0}m", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Build-Piece Physics", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece Collider count: {_counts.PieceCollider}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece enabled Collider count: {_counts.PieceEnabledCollider}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece Rigidbody count: {_counts.PieceRigidbody}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece active Rigidbody count: {_counts.PieceActiveRigidbody}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Build-Piece Effects", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece Light count: {_counts.PieceLight}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece enabled Light count: {_counts.PieceEnabledLight}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece ParticleSystem count: {_counts.PieceParticleSystem}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece active ParticleSystem count: {_counts.PieceActiveParticleSystem}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Build-piece AudioSource count: {_counts.PieceAudioSource}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		GUILayout.Label("Fire Optimization Candidates", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Fire candidates: {_counts.FireCandidates}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Renderer-visible fire candidates: {_counts.RendererVisibleFireCandidates}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Occluded fire candidates: {_counts.OccludedFireCandidates}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Relevant fire candidates: {_counts.RelevantFireCandidates}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Hidden/irrelevant fire candidates: {_counts.HiddenOrIrrelevantFireCandidates}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Fire culling mode: {_fireCullingMode.Value}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Visibility culling: {_useFireVisibilityCulling.Value}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Occlusion culling: {_useFireOcclusionCulling.Value}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Optimized fire pieces: {_counts.OptimizedFirePieces}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"StaticLight fire pieces: {_counts.StaticLightFirePieces}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"FullCull fire pieces: {_counts.FullCullFirePieces}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Fire proxy lights active: {_counts.FireProxyLightsActive}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Original fire lights disabled: {_counts.FireOriginalLightsDisabled}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Fire particles stopped: {_counts.FireParticlesStopped}", Array.Empty<GUILayoutOption>());
		GUILayout.Label($"Fire shadows disabled: {_counts.FireShadowsDisabled}", Array.Empty<GUILayoutOption>());
		GUILayout.Space(8f);
		if (GUILayout.Button("Poll now", Array.Empty<GUILayoutOption>()))
		{
			_counts = PollCounts();
		}
		GUILayout.EndScrollView();
		GUI.DragWindow();
	}

	private Texture2D MakeTexture(int width, int height, Color color)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		Color[] array = (Color[])(object)new Color[width * height];
		for (int i = 0; i < array.Length; i++)
		{
			array[i] = color;
		}
		Texture2D val = new Texture2D(width, height);
		val.SetPixels(array);
		val.Apply();
		return val;
	}

	private Counts PollCounts()
	{
		//IL_0254: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Unknown result type (might be due to invalid IL or missing references)
		//IL_037b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0384: Unknown result type (might be due to invalid IL or missing references)
		Piece[] array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
		WearNTear[] array2 = Object.FindObjectsByType<WearNTear>((FindObjectsSortMode)0);
		ZNetView[] array3 = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
		MeshRenderer[] array4 = Object.FindObjectsByType<MeshRenderer>((FindObjectsSortMode)0);
		Collider[] array5 = Object.FindObjectsByType<Collider>((FindObjectsSortMode)0);
		LODGroup[] array6 = Object.FindObjectsByType<LODGroup>((FindObjectsSortMode)0);
		Light[] array7 = Object.FindObjectsByType<Light>((FindObjectsSortMode)0);
		ParticleSystem[] array8 = Object.FindObjectsByType<ParticleSystem>((FindObjectsSortMode)0);
		AudioSource[] array9 = Object.FindObjectsByType<AudioSource>((FindObjectsSortMode)0);
		Rigidbody[] array10 = Object.FindObjectsByType<Rigidbody>((FindObjectsSortMode)0);
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		int num6 = 0;
		MeshRenderer[] array11 = array4;
		foreach (MeshRenderer val in array11)
		{
			if (!((Object)(object)val == (Object)null))
			{
				if (((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy)
				{
					num++;
				}
				if (((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy && ((Renderer)val).isVisible)
				{
					num2++;
				}
			}
		}
		Collider[] array12 = array5;
		foreach (Collider val2 in array12)
		{
			if ((Object)(object)val2 != (Object)null && val2.enabled && ((Component)val2).gameObject.activeInHierarchy)
			{
				num3++;
			}
		}
		Light[] array13 = array7;
		foreach (Light val3 in array13)
		{
			if ((Object)(object)val3 != (Object)null && ((Behaviour)val3).enabled && ((Component)val3).gameObject.activeInHierarchy)
			{
				num4++;
			}
		}
		ParticleSystem[] array14 = array8;
		foreach (ParticleSystem val4 in array14)
		{
			if ((Object)(object)val4 != (Object)null && ((Component)val4).gameObject.activeInHierarchy && val4.IsAlive(true))
			{
				num5++;
			}
		}
		Rigidbody[] array15 = array10;
		foreach (Rigidbody val5 in array15)
		{
			if ((Object)(object)val5 != (Object)null && ((Component)val5).gameObject.activeInHierarchy && !val5.IsSleeping())
			{
				num6++;
			}
		}
		int num7 = 0;
		int num8 = 0;
		int num9 = 0;
		int num10 = 0;
		int num11 = 0;
		int num12 = 0;
		int num13 = 0;
		int num14 = 0;
		int num15 = 0;
		int num16 = 0;
		int num17 = 0;
		int num18 = 0;
		int num19 = 0;
		Vector3 val6 = Vector3.zero;
		bool flag = (Object)(object)Player.m_localPlayer != (Object)null;
		if (flag)
		{
			val6 = ((Component)Player.m_localPlayer).transform.position;
		}
		float num20 = Mathf.Max(1f, _nearDistance.Value);
		float num21 = Mathf.Max(num20, _mediumDistance.Value);
		float num22 = Mathf.Max(num21, _farDistance.Value);
		float num23 = Mathf.Max(num22, _veryFarDistance.Value);
		float num24 = Mathf.Max(num23, _extremeDistance.Value);
		int num25 = 0;
		int num26 = 0;
		int num27 = 0;
		int num28 = 0;
		int num29 = 0;
		int num30 = 0;
		float num31 = float.MaxValue;
		float num32 = 0f;
		int num33 = 0;
		int num34 = 0;
		int num35 = 0;
		int num36 = 0;
		int num37 = 0;
		bool flag2 = _enableOptimizations.Value && _fireCullingMode.Value != FireCullingMode.Off;
		Camera mainCamera = (flag2 ? null : Camera.main);
		Piece[] array16 = array;
		foreach (Piece val7 in array16)
		{
			if ((Object)(object)val7 == (Object)null)
			{
				continue;
			}
			if (flag)
			{
				float num38 = Vector3.Distance(val6, ((Component)val7).transform.position);
				if (num38 <= num20)
				{
					num25++;
				}
				if (num38 <= num21)
				{
					num26++;
				}
				if (num38 <= num22)
				{
					num27++;
				}
				if (num38 <= num23)
				{
					num28++;
				}
				if (num38 <= num24)
				{
					num29++;
				}
				else
				{
					num30++;
				}
				if (num38 < num31)
				{
					num31 = num38;
				}
				if (num38 > num32)
				{
					num32 = num38;
				}
			}
			MeshRenderer[] componentsInChildren = ((Component)val7).GetComponentsInChildren<MeshRenderer>(true);
			Collider[] componentsInChildren2 = ((Component)val7).GetComponentsInChildren<Collider>(true);
			LODGroup[] componentsInChildren3 = ((Component)val7).GetComponentsInChildren<LODGroup>(true);
			Light[] componentsInChildren4 = ((Component)val7).GetComponentsInChildren<Light>(true);
			ParticleSystem[] componentsInChildren5 = ((Component)val7).GetComponentsInChildren<ParticleSystem>(true);
			if (!flag2)
			{
				Light[] originalLights = GetOriginalLights(componentsInChildren4);
				if (IsFireCandidate(originalLights, componentsInChildren5))
				{
					num33++;
					bool flag3 = IsPieceVisible(componentsInChildren);
					bool flag4 = flag3 && IsFireOccludedFromCamera(val7, originalLights, componentsInChildren, mainCamera);
					bool flag5 = flag3 && !flag4;
					if (flag3)
					{
						num34++;
					}
					if (flag4)
					{
						num35++;
					}
					if (flag5)
					{
						num36++;
					}
					else
					{
						num37++;
					}
				}
			}
			AudioSource[] componentsInChildren6 = ((Component)val7).GetComponentsInChildren<AudioSource>(true);
			Rigidbody[] componentsInChildren7 = ((Component)val7).GetComponentsInChildren<Rigidbody>(true);
			num7 += componentsInChildren.Length;
			num10 += componentsInChildren2.Length;
			num12 += componentsInChildren3.Length;
			num13 += componentsInChildren4.Length;
			num15 += componentsInChildren5.Length;
			num17 += componentsInChildren6.Length;
			num18 += componentsInChildren7.Length;
			MeshRenderer[] array17 = componentsInChildren;
			foreach (MeshRenderer val8 in array17)
			{
				if (!((Object)(object)val8 == (Object)null))
				{
					if (((Renderer)val8).enabled && ((Component)val8).gameObject.activeInHierarchy)
					{
						num8++;
					}
					if (((Renderer)val8).enabled && ((Component)val8).gameObject.activeInHierarchy && ((Renderer)val8).isVisible)
					{
						num9++;
					}
				}
			}
			Collider[] array18 = componentsInChildren2;
			foreach (Collider val9 in array18)
			{
				if ((Object)(object)val9 != (Object)null && val9.enabled && ((Component)val9).gameObject.activeInHierarchy)
				{
					num11++;
				}
			}
			Light[] array19 = componentsInChildren4;
			foreach (Light val10 in array19)
			{
				if ((Object)(object)val10 != (Object)null && ((Behaviour)val10).enabled && ((Component)val10).gameObject.activeInHierarchy)
				{
					num14++;
				}
			}
			ParticleSystem[] array20 = componentsInChildren5;
			foreach (ParticleSystem val11 in array20)
			{
				if ((Object)(object)val11 != (Object)null && ((Component)val11).gameObject.activeInHierarchy && val11.IsAlive(true))
				{
					num16++;
				}
			}
			Rigidbody[] array21 = componentsInChildren7;
			foreach (Rigidbody val12 in array21)
			{
				if ((Object)(object)val12 != (Object)null && ((Component)val12).gameObject.activeInHierarchy && !val12.IsSleeping())
				{
					num19++;
				}
			}
		}
		if (!flag || array.Length == 0)
		{
			num31 = 0f;
			num32 = 0f;
		}
		FireMetrics fireMetrics2;
		if (!flag2)
		{
			FireMetrics fireMetrics = default(FireMetrics);
			fireMetrics.FireCandidates = num33;
			fireMetrics.RendererVisibleFireCandidates = num34;
			fireMetrics.OccludedFireCandidates = num35;
			fireMetrics.RelevantFireCandidates = num36;
			fireMetrics.HiddenOrIrrelevantFireCandidates = num37;
			fireMetrics2 = fireMetrics;
		}
		else
		{
			fireMetrics2 = _fireMetrics;
		}
		FireMetrics metrics = fireMetrics2;
		if (!flag2)
		{
			AddFireOptimizationStateMetrics(ref metrics);
		}
		Counts result = default(Counts);
		result.Pieces = array.Length;
		result.WearNTear = array2.Length;
		result.ZNetView = array3.Length;
		result.MeshRenderer = array4.Length;
		result.EnabledMeshRenderer = num;
		result.VisibleMeshRenderer = num2;
		result.Collider = array5.Length;
		result.EnabledCollider = num3;
		result.LODGroup = array6.Length;
		result.Light = array7.Length;
		result.EnabledLight = num4;
		result.ParticleSystem = array8.Length;
		result.ActiveParticleSystem = num5;
		result.AudioSource = array9.Length;
		result.ActiveRigidbody = num6;
		result.PieceMeshRenderer = num7;
		result.PieceEnabledMeshRenderer = num8;
		result.PieceVisibleMeshRenderer = num9;
		result.PieceCollider = num10;
		result.PieceEnabledCollider = num11;
		result.PieceLODGroup = num12;
		result.PieceLight = num13;
		result.PieceEnabledLight = num14;
		result.PieceParticleSystem = num15;
		result.PieceActiveParticleSystem = num16;
		result.PieceAudioSource = num17;
		result.PieceRigidbody = num18;
		result.PieceActiveRigidbody = num19;
		result.PiecesWithinNear = num25;
		result.PiecesWithinMedium = num26;
		result.PiecesWithinFar = num27;
		result.PiecesWithinVeryFar = num28;
		result.PiecesWithinExtreme = num29;
		result.PiecesBeyondExtreme = num30;
		result.NearestPieceDistance = num31;
		result.FarthestPieceDistance = num32;
		result.FireCandidates = metrics.FireCandidates;
		result.RendererVisibleFireCandidates = metrics.RendererVisibleFireCandidates;
		result.OccludedFireCandidates = metrics.OccludedFireCandidates;
		result.RelevantFireCandidates = metrics.RelevantFireCandidates;
		result.HiddenOrIrrelevantFireCandidates = metrics.HiddenOrIrrelevantFireCandidates;
		result.OptimizedFirePieces = metrics.OptimizedFirePieces;
		result.StaticLightFirePieces = metrics.StaticLightFirePieces;
		result.FullCullFirePieces = metrics.FullCullFirePieces;
		result.FireProxyLightsActive = metrics.FireProxyLightsActive;
		result.FireOriginalLightsDisabled = metrics.FireOriginalLightsDisabled;
		result.FireParticlesStopped = metrics.FireParticlesStopped;
		result.FireShadowsDisabled = metrics.FireShadowsDisabled;
		return result;
	}
}