Decompiled source of ValheimBuildOptimization v0.5.5

plugins/ValheimBuildOptimization.dll

Decompiled a month 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 HarmonyLib;
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("0.5.3.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.5.3.0")]
namespace BuildPieceProfiler;

[BepInPlugin("valheim.buildpieceprofiler", "Valheim Build Optimization", "0.5.3")]
public class BuildPieceProfilerPlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(Piece), "Awake")]
	private static class PieceAwakeOptimizationLifecyclePatch
	{
		private static void Postfix(Piece __instance)
		{
			NotifyPieceLoadedOrChanged(__instance, forceRefresh: false);
		}
	}

	[HarmonyPatch(typeof(Piece), "OnPlaced")]
	private static class PieceOnPlacedOptimizationLifecyclePatch
	{
		private static void Postfix(Piece __instance)
		{
			NotifyPieceLoadedOrChanged(__instance, forceRefresh: true);
		}
	}

	[HarmonyPatch(typeof(Piece), "OnDestroy")]
	private static class PieceOnDestroyOptimizationLifecyclePatch
	{
		private static void Prefix(Piece __instance)
		{
			NotifyPieceDestroyed(__instance);
		}
	}

	[HarmonyPatch(typeof(Fireplace), "Awake")]
	private static class FireplaceAwakeOptimizationLifecyclePatch
	{
		private static void Postfix()
		{
			if ((Object)(object)_instance != (Object)null && _instance._enableFireOptimizations != null && _instance._enableFireOptimizations.Value)
			{
				_instance._fireCandidateRefreshRequested = true;
			}
		}
	}

	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	private static class WearNTearUpdateSupportPatch
	{
		private static bool Prefix(WearNTear __instance)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			ApplyBypassedSupport(__instance);
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "GetSupport")]
	private static class WearNTearGetSupportPatch
	{
		private static bool Prefix(WearNTear __instance, ref float __result)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			__result = GetBypassedSupportValue();
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "HaveSupport")]
	private static class WearNTearHaveSupportPatch
	{
		private static bool Prefix(ref bool __result)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			__result = true;
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "UpdateCover")]
	private static class WearNTearUpdateCoverPatch
	{
		private static bool Prefix(WearNTear __instance)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			ApplyDryRoofedState(__instance);
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "HaveRoof")]
	private static class WearNTearHaveRoofPatch
	{
		private static bool Prefix(WearNTear __instance, ref bool __result)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			__result = true;
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "HaveAshRoof")]
	private static class WearNTearHaveAshRoofPatch
	{
		private static bool Prefix(WearNTear __instance, ref bool __result)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			__result = true;
			return false;
		}
	}

	[HarmonyPatch(typeof(WearNTear), "IsWet")]
	private static class WearNTearIsWetPatch
	{
		private static bool Prefix(WearNTear __instance, ref bool __result)
		{
			if (!IsWearNTearOptimizationEnabled())
			{
				return true;
			}
			__result = false;
			return false;
		}
	}

	private enum AppliedFireMode
	{
		StaticLight,
		FullCull
	}

	private class FireCandidate
	{
		public Piece Piece;

		public Fireplace Source;

		public MeshRenderer[] Renderers;

		public Light[] OriginalLights;

		public ParticleSystem[] Particles;

		public bool HasOcclusionResult;

		public bool CachedOccluded;

		public float LastOcclusionCheckTime;

		public int OcclusionCheckGeneration;

		public float LastRelevantTime;

		public float LastIrrelevantTime;
	}

	private class FireOptimizationState
	{
		public Piece Piece;

		public Fireplace Source;

		public AppliedFireMode AppliedMode;

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

		public readonly Dictionary<Light, int> OriginalLightCullingMask = new Dictionary<Light, int>();

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

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

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

		public readonly Dictionary<Behaviour, bool> OriginalBehaviourEnabled = new Dictionary<Behaviour, bool>();

		public readonly HashSet<ParticleSystem> ForcedStoppedParticles = new HashSet<ParticleSystem>();

		public GameObject ProxyLightObject;

		public Light ProxyLight;
	}

	private struct ClusterProxyKey : IEquatable<ClusterProxyKey>
	{
		public int X;

		public int Y;

		public int Z;

		public bool Equals(ClusterProxyKey other)
		{
			return X == other.X && Y == other.Y && Z == other.Z;
		}

		public override bool Equals(object obj)
		{
			return obj is ClusterProxyKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			int num = 17;
			num = num * 31 + X;
			num = num * 31 + Y;
			return num * 31 + Z;
		}
	}

	private class RaycastHitDistanceComparer : IComparer<RaycastHit>
	{
		public int Compare(RaycastHit x, RaycastHit y)
		{
			return ((RaycastHit)(ref x)).distance.CompareTo(((RaycastHit)(ref y)).distance);
		}
	}

	private class ClusterProxyLightState
	{
		public GameObject ProxyLightObject;

		public Light ProxyLight;

		public bool ActiveThisUpdate;

		public int MemberCount;

		public Vector3 PositionSum;

		public Color ColorSum;

		public float IntensitySum;

		public float RangeMax;

		public Quaternion Rotation;

		public bool HasRotation;
	}

	private enum FireOptimizationMode
	{
		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 LightOffenderSnapshot
	{
		public string Name;

		public float Distance;

		public int EnabledLights;

		public int ShadowLights;

		public int ActiveParticles;

		public int LightUpdateBehaviours;

		public float MaxRange;

		public float TotalIntensity;

		public float Score;
	}

	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 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 List<LightOffenderSnapshot> TopLightOffenders;
	}

	private enum ColliderClusterPieceEligibility
	{
		Eligible,
		Ineligible,
		Interactive,
		NameFilter
	}

	private struct ColliderClusterCellKey : IEquatable<ColliderClusterCellKey>
	{
		public int X;

		public int Y;

		public int Z;

		public bool Equals(ColliderClusterCellKey other)
		{
			return X == other.X && Y == other.Y && Z == other.Z;
		}

		public override bool Equals(object obj)
		{
			return obj is ColliderClusterCellKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			int num = 17;
			num = num * 31 + X;
			num = num * 31 + Y;
			return num * 31 + Z;
		}

		public override string ToString()
		{
			return $"{X},{Y},{Z}";
		}
	}

	private struct ColliderClusterGroupKey : IEquatable<ColliderClusterGroupKey>
	{
		public int Layer;

		public PhysicsMaterial Material;

		public bool Equals(ColliderClusterGroupKey other)
		{
			return Layer == other.Layer && (Object)(object)Material == (Object)(object)other.Material;
		}

		public override bool Equals(object obj)
		{
			return obj is ColliderClusterGroupKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			int layer = Layer;
			return (layer * 397) ^ (((Object)(object)Material != (Object)null) ? ((Object)Material).GetInstanceID() : 0);
		}
	}

	internal class ColliderClusterSource
	{
		public BoxCollider Collider;

		public Piece Piece;

		public WearNTear WearNTear;

		public bool OriginalEnabled;

		public Bounds WorldBounds;
	}

	private class ColliderMergeRun
	{
		public Bounds WorldBounds;

		public readonly List<ColliderClusterSource> Sources = new List<ColliderClusterSource>();
	}

	private class ColliderClusterOutput
	{
		public GameObject GameObject;

		public BoxCollider Collider;

		public List<ColliderClusterSource> Sources;

		public Bounds WorldBounds;
	}

	private class ColliderClusterCell
	{
		public ColliderClusterCellKey Key;

		public readonly HashSet<Piece> Pieces = new HashSet<Piece>();

		public readonly List<ColliderClusterOutput> Outputs = new List<ColliderClusterOutput>();

		public GameObject Root;

		public bool Dirty;

		public bool Queued;

		public bool ClusterActive;

		public int PiecesConsidered;

		public int PiecesEligible;

		public int BoxCollidersConsidered;

		public int EligibleBoxColliders;

		public int ExcludedTriggers;

		public int ExcludedRigidbodies;

		public int ExcludedInteractivePieces;

		public int ExcludedNamePieces;

		public int ExcludedNonBoxColliders;

		public int GroupsBelowMinimum;

		public int PotentialClusterColliders;

		public int PotentialClusteredBoxes;

		public Bounds ClusterBounds;

		public bool HasClusterBounds;
	}

	private struct ColliderClusterMetrics
	{
		public bool ProfilingEnabled;

		public bool ClusteringRequested;

		public bool ClusteringActive;

		public bool WearNTearRequirementMet;

		public int TrackedPieces;

		public int Cells;

		public int DirtyCells;

		public int DiscoveryRemaining;

		public int ActiveClusterCells;

		public int PiecesConsidered;

		public int PiecesEligible;

		public int BoxCollidersConsidered;

		public int EligibleBoxColliders;

		public int ClusterColliders;

		public int ClusteredSourceBoxes;

		public int OriginalCollidersDisabled;

		public int EstimatedColliderReduction;

		public int PotentialClusterColliders;

		public int PotentialClusteredBoxes;

		public int PotentialColliderReduction;

		public int ExcludedTriggers;

		public int ExcludedRigidbodies;

		public int ExcludedInteractivePieces;

		public int ExcludedNamePieces;

		public int ExcludedNonBoxColliders;

		public int GroupsBelowMinimum;
	}

	private static class ZdoExtraDataSetNetworkProfilerPatch
	{
		internal static IEnumerable<MethodBase> TargetMethods()
		{
			Type type = AccessTools.TypeByName("ZDOExtraData");
			if (type == null)
			{
				yield break;
			}
			foreach (MethodInfo method in AccessTools.GetDeclaredMethods(type))
			{
				ParameterInfo[] parameters = method.GetParameters();
				if (method.Name == "Set" && method.ReturnType == typeof(bool) && parameters.Length >= 3 && parameters[0].ParameterType == typeof(ZDOID))
				{
					yield return method;
				}
			}
		}

		internal static void Postfix(ZDOID zid, bool __result)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (_networkProfilerHooksActive)
			{
				_instance?.RecordNetworkZdoWrite(zid, __result);
			}
		}
	}

	private static class ZNetViewInvokeRpcNetworkProfilerPatch
	{
		internal static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MethodInfo method in AccessTools.GetDeclaredMethods(typeof(ZNetView)))
			{
				if (method.Name == "InvokeRPC")
				{
					yield return method;
				}
			}
		}

		internal static void Prefix(ZNetView __instance, string method)
		{
			if (_networkProfilerHooksActive)
			{
				_instance?.RecordNetworkRpc(__instance, method);
			}
		}
	}

	private class NetworkPieceState
	{
		public Piece Piece;

		public ZNetView View;

		public ZDO Zdo;

		public ZDOID ZdoId;

		public string Name;

		public uint LastDataRevision;

		public ushort LastOwnerRevision;

		public long LastOwner;

		public long WriteAttempts;

		public long ChangedWrites;

		public long RpcCalls;

		public long ObservedRevisionChanges;

		public long OwnerChanges;

		public float LastActivityTime = float.MinValue;

		public bool IsStaticStructure;
	}

	private struct NetworkWriterSnapshot
	{
		public string Name;

		public long WriteAttempts;

		public long ChangedWrites;

		public long RevisionChanges;

		public long RpcCalls;

		public bool IsStaticStructure;
	}

	private struct NetworkRpcSnapshot
	{
		public string Method;

		public int Count;
	}

	private struct NetworkIdleMetrics
	{
		public int TrackedPieces;

		public int DiscoveryRemaining;

		public int OwnedByLocal;

		public int OwnedByOther;

		public int Unowned;

		public int ActiveWriters;

		public int StaticActiveWriters;

		public int DynamicActiveWriters;

		public long StaticWriteAttempts;

		public long StaticChangedWrites;

		public long DynamicWriteAttempts;

		public long DynamicChangedWrites;

		public long WriteAttempts;

		public long ChangedWrites;

		public long RedundantWrites;

		public long RpcCalls;

		public long ObservedRevisionChanges;

		public long ObservedOwnerChanges;

		public int SentZdos;

		public int ReceivedZdos;

		public int ClientChangeQueue;
	}

	private struct NetworkMetricsAggregate
	{
		public int OwnedByLocal;

		public int OwnedByOther;

		public int Unowned;

		public int ActiveWriters;

		public int StaticActiveWriters;

		public int DynamicActiveWriters;

		public long StaticWriteAttempts;

		public long StaticChangedWrites;

		public long DynamicWriteAttempts;

		public long DynamicChangedWrites;
	}

	[HarmonyPatch(typeof(WearNTear), "Highlight")]
	private static class WearNTearHighlightOptimizationPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			BuildPieceProfilerPlugin instance = _instance;
			if (!((Object)(object)instance == (Object)null) && instance.ShouldHandleRendererOrStaticSleepEvents())
			{
				Piece piece = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Piece>() : null);
				instance?.NotifyRendererBatchHighlight(piece, 0.15f);
				instance?.NotifyStaticSleepPieceChanged(piece);
			}
		}
	}

	[HarmonyPatch(typeof(WearNTear), "Damage")]
	private static class WearNTearDamageOptimizationPatch
	{
		private static void Postfix(WearNTear __instance)
		{
			BuildPieceProfilerPlugin instance = _instance;
			if (!((Object)(object)instance == (Object)null) && instance.ShouldHandleRendererOrStaticSleepEvents())
			{
				Piece piece = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Piece>() : null);
				instance?.NotifyRendererBatchVisualChange(piece, 0.35f);
				instance?.NotifyStaticSleepPieceChanged(piece);
			}
		}
	}

	[HarmonyPatch(typeof(WearNTear), "Repair")]
	private static class WearNTearRepairOptimizationPatch
	{
		private static void Postfix(WearNTear __instance)
		{
			BuildPieceProfilerPlugin instance = _instance;
			if (!((Object)(object)instance == (Object)null) && instance.ShouldHandleRendererOrStaticSleepEvents())
			{
				Piece piece = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Piece>() : null);
				instance?.NotifyRendererBatchVisualChange(piece, 0.35f);
				instance?.NotifyStaticSleepPieceChanged(piece);
			}
		}
	}

	[HarmonyPatch(typeof(WearNTear), "RPC_HealthChanged")]
	private static class WearNTearHealthChangedOptimizationPatch
	{
		private static void Postfix(WearNTear __instance)
		{
			BuildPieceProfilerPlugin instance = _instance;
			if (!((Object)(object)instance == (Object)null) && instance.ShouldHandleRendererOrStaticSleepEvents())
			{
				Piece piece = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponentInParent<Piece>() : null);
				instance?.NotifyRendererBatchVisualChange(piece, 0.35f);
				instance?.NotifyStaticSleepPieceChanged(piece);
			}
		}
	}

	private struct RendererBatchCellKey : IEquatable<RendererBatchCellKey>
	{
		public int X;

		public int Y;

		public int Z;

		public bool Equals(RendererBatchCellKey other)
		{
			return X == other.X && Y == other.Y && Z == other.Z;
		}

		public override bool Equals(object obj)
		{
			return obj is RendererBatchCellKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			int num = 17;
			num = num * 31 + X;
			num = num * 31 + Y;
			return num * 31 + Z;
		}

		public override string ToString()
		{
			return $"{X},{Y},{Z}";
		}
	}

	private enum RendererBatchPieceEligibility
	{
		Eligible,
		TemporarilyExcluded,
		NonStructural,
		Interactive,
		Animated,
		Effects,
		ExcludedByName
	}

	private enum RendererBatchRendererEligibility
	{
		Eligible,
		Inactive,
		StaticBatch,
		Lod,
		PropertyBlock,
		MeshLayout,
		Lightmap,
		Material
	}

	private struct RendererBatchGroupKey : IEquatable<RendererBatchGroupKey>
	{
		public Material Material;

		public ShadowCastingMode ShadowCastingMode;

		public bool ReceiveShadows;

		public LightProbeUsage LightProbeUsage;

		public ReflectionProbeUsage ReflectionProbeUsage;

		public Transform ProbeAnchor;

		public int Layer;

		public MotionVectorGenerationMode MotionVectorGenerationMode;

		public bool AllowOcclusionWhenDynamic;

		public int SortingLayerId;

		public int SortingOrder;

		public bool Equals(RendererBatchGroupKey other)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			return (Object)(object)Material == (Object)(object)other.Material && ShadowCastingMode == other.ShadowCastingMode && ReceiveShadows == other.ReceiveShadows && LightProbeUsage == other.LightProbeUsage && ReflectionProbeUsage == other.ReflectionProbeUsage && (Object)(object)ProbeAnchor == (Object)(object)other.ProbeAnchor && Layer == other.Layer && MotionVectorGenerationMode == other.MotionVectorGenerationMode && AllowOcclusionWhenDynamic == other.AllowOcclusionWhenDynamic && SortingLayerId == other.SortingLayerId && SortingOrder == other.SortingOrder;
		}

		public override bool Equals(object obj)
		{
			return obj is RendererBatchGroupKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected I4, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected I4, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected I4, but got Unknown
			//IL_009c: 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_00a3: Expected I4, but got Unknown
			int num = (((Object)(object)Material != (Object)null) ? ((Object)Material).GetInstanceID() : 0);
			num = (num * 397) ^ ShadowCastingMode;
			num = (num * 397) ^ ReceiveShadows.GetHashCode();
			num = (num * 397) ^ LightProbeUsage;
			num = (num * 397) ^ ReflectionProbeUsage;
			num = (num * 397) ^ (((Object)(object)ProbeAnchor != (Object)null) ? ((Object)ProbeAnchor).GetInstanceID() : 0);
			num = (num * 397) ^ Layer;
			num = (num * 397) ^ MotionVectorGenerationMode;
			num = (num * 397) ^ AllowOcclusionWhenDynamic.GetHashCode();
			num = (num * 397) ^ SortingLayerId;
			return (num * 397) ^ SortingOrder;
		}
	}

	private struct RendererShadowBatchGroupKey : IEquatable<RendererShadowBatchGroupKey>
	{
		public Shader Shader;

		public int Layer;

		public int CullMode;

		public bool Equals(RendererShadowBatchGroupKey other)
		{
			return (Object)(object)Shader == (Object)(object)other.Shader && Layer == other.Layer && CullMode == other.CullMode;
		}

		public override bool Equals(object obj)
		{
			return obj is RendererShadowBatchGroupKey other && Equals(other);
		}

		public override int GetHashCode()
		{
			int num = (((Object)(object)Shader != (Object)null) ? ((Object)Shader).GetInstanceID() : 0);
			num = (num * 397) ^ Layer;
			return (num * 397) ^ CullMode;
		}
	}

	private class RendererBatchSource
	{
		public MeshRenderer Renderer;

		public Mesh Mesh;

		public int VertexCount;

		public RendererBatchGroupKey GroupKey;

		public LODGroup LodGroup;

		public Piece Piece;
	}

	private class RendererShadowSourceGeometry
	{
		public Vector3[] Vertices;

		public int[] Triangles;
	}

	private class RendererBatchOutput
	{
		public GameObject GameObject;

		public MeshRenderer Renderer;

		public Mesh Mesh;

		public int VertexCount;

		public RendererBatchGroupKey GroupKey;

		public List<RendererBatchSource> Sources;
	}

	private class RendererShadowBatchOutput
	{
		public GameObject GameObject;

		public Mesh Mesh;

		public int VertexCount;
	}

	private class RendererBatchCell
	{
		public RendererBatchCellKey Key;

		public readonly HashSet<Piece> Pieces = new HashSet<Piece>();

		public readonly Dictionary<MeshRenderer, bool> OriginalRendererEnabled = new Dictionary<MeshRenderer, bool>();

		public readonly HashSet<LODGroup> ForcedLodGroups = new HashSet<LODGroup>();

		public readonly List<RendererBatchOutput> Outputs = new List<RendererBatchOutput>();

		public readonly List<RendererShadowBatchOutput> ShadowOutputs = new List<RendererShadowBatchOutput>();

		public readonly Dictionary<Piece, List<MeshRenderer>> SourceRenderersByPiece = new Dictionary<Piece, List<MeshRenderer>>();

		public GameObject Root;

		public bool Dirty;

		public bool Queued;

		public float RebuildAfter;

		public int EligibleRendererCount;

		public int UnreadableMeshesSkipped;

		public int PropertyBlocksSkipped;

		public int PiecesConsidered;

		public int PiecesEligible;

		public int PiecesExcludedNonStructural;

		public int PiecesExcludedInteractive;

		public int PiecesExcludedAnimated;

		public int PiecesExcludedEffects;

		public int PiecesExcludedByName;

		public int RenderersExcludedStaticBatch;

		public int RenderersExcludedLod;

		public int RenderersExcludedMeshLayout;

		public int RenderersExcludedLightmap;

		public int RenderersExcludedMaterial;

		public int GroupsBelowMinimum;

		public int ShadowCastingVisibleBatches;

		public int ShadowEligibleVisibleBatches;

		public int ShadowSourceRenderersConsolidated;

		public int ShadowDrawCallsAvoided;

		public int ShadowExcludedCastingModeBatches;

		public int ShadowExcludedMaterialBatches;

		public int ShadowGroupsBelowMinimum;

		public int ShadowRejectedVisibleBatchCount;

		public int ShadowRejectedDrawBenefit;

		public int ShadowRejectedVertexBenefit;

		public int ShadowRejectedBounds;

		public int ShadowOriginalVerticesReplaced;

		public int ShadowSimplifiedSources;
	}

	private struct RendererLodCacheEntry
	{
		public LODGroup LodGroup;

		public int LodIndex;
	}

	private struct RendererBatchMetrics
	{
		public int TrackedPieces;

		public int Cells;

		public int DirtyCells;

		public int DiscoveryRemaining;

		public int SourceRenderersDisabled;

		public int CombinedRenderers;

		public int CombinedVertices;

		public int EligibleSourceRenderers;

		public int UnreadableMeshesSkipped;

		public int PropertyBlocksSkipped;

		public int PiecesConsidered;

		public int PiecesEligible;

		public int PiecesExcludedNonStructural;

		public int PiecesExcludedInteractive;

		public int PiecesExcludedAnimated;

		public int PiecesExcludedEffects;

		public int PiecesExcludedByName;

		public int RenderersExcludedStaticBatch;

		public int RenderersExcludedLod;

		public int RenderersExcludedMeshLayout;

		public int RenderersExcludedLightmap;

		public int RenderersExcludedMaterial;

		public int GroupsBelowMinimum;

		public int ShadowCastingVisibleBatches;

		public int ShadowEligibleVisibleBatches;

		public int ShadowClusterRenderers;

		public int ShadowClusterVertices;

		public int ShadowSourceRenderersConsolidated;

		public int ShadowDrawCallsAvoided;

		public int ShadowExcludedCastingModeBatches;

		public int ShadowExcludedMaterialBatches;

		public int ShadowGroupsBelowMinimum;

		public int ShadowRejectedVisibleBatchCount;

		public int ShadowRejectedDrawBenefit;

		public int ShadowRejectedVertexBenefit;

		public int ShadowRejectedBounds;

		public int ShadowOriginalVerticesReplaced;

		public int ShadowSimplifiedSources;
	}

	private class StaticSleepPieceState
	{
		public Piece Piece;

		public List<StaticSleepComponentState> Components;

		public int Generation;

		public float SleepAfter;

		public bool IsSleeping;

		public bool SchedulePending;
	}

	private class StaticSleepComponentState
	{
		public MonoBehaviour Behaviour;

		public StaticSleepTypeMetrics TypeMetrics;

		public bool OriginalEnabled;

		public bool DisabledByUs;
	}

	private class StaticSleepTypeMetrics
	{
		public Type Type;

		public int Total;

		public int EnabledAtDiscovery;

		public int Whitelisted;

		public int Candidates;

		public int Sleeping;
	}

	private struct StaticSleepProfileRecord
	{
		public StaticSleepTypeMetrics TypeMetrics;

		public bool EnabledAtDiscovery;

		public bool Whitelisted;

		public bool Candidate;
	}

	private struct StaticSleepScheduleEntry
	{
		public StaticSleepPieceState State;

		public int Generation;

		public float DueTime;
	}

	private struct StaticSleepTypeSnapshot
	{
		public string Name;

		public int Total;

		public int EnabledAtDiscovery;

		public int Candidates;

		public int Sleeping;
	}

	private struct StaticSleepMetrics
	{
		public int ProfiledPieces;

		public int DiscoveryRemaining;

		public int UpdateLoopComponents;

		public int EnabledAtDiscovery;

		public int CandidatePieces;

		public int CandidateComponents;

		public int SleepingComponents;

		public int WakeEvents;
	}

	public const string PluginGuid = "valheim.buildpieceprofiler";

	public const string PluginName = "Valheim Build Optimization";

	public const string PluginVersion = "0.5.3";

	private const string StaticFireLightProxyName = "BuildPieceProfiler_StaticFireLightProxy";

	private const string ProxyLightPoolRootName = "BuildPieceProfiler_ProxyLightPool";

	private const float DefaultBypassedSupportValue = 1000000f;

	private static BuildPieceProfilerPlugin _instance;

	private static readonly FieldInfo PieceAllPiecesField = AccessTools.Field(typeof(Piece), "s_allPieces");

	private static bool _wearNTearOptimizationActive;

	private static float _cachedBypassedSupportValue = 1000000f;

	private Harmony _harmony;

	private readonly HashSet<Piece> _loadedPieceRegistry = new HashSet<Piece>();

	private Piece[] _loadedPieceSnapshot;

	private bool _loadedPieceSnapshotDirty = true;

	private bool _loadedPieceRegistryInitialized;

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

	private Rect _windowRect;

	private Vector2 _scrollPosition = Vector2.zero;

	private readonly HashSet<string> _expandedProfilerSections = new HashSet<string>();

	private GUIStyle _profilerSectionStyle;

	private GUIStyle _profilerStatStyle;

	private GUIStyle _profilerTooltipStyle;

	private static readonly string[] ProfilerSectionIds = new string[15]
	{
		"overview", "global-scene", "global-rendering", "global-physics", "global-effects", "piece-rendering", "piece-physics", "piece-effects", "fire", "renderer-batching",
		"shadow", "static-sleep", "collider", "network", "light-offenders"
	};

	private bool _showOverlay;

	private float _nextPollTime;

	private bool _automaticProfilerLimitWarningLogged;

	private const int MaximumAutomaticProfilerPieces = 5000;

	private static readonly FieldRef<WearNTear, float> WearNTearSupportRef = AccessTools.FieldRefAccess<WearNTear, float>("m_support");

	private static readonly FieldRef<WearNTear, bool> WearNTearRainWetRef = AccessTools.FieldRefAccess<WearNTear, bool>("m_rainWet");

	private static readonly FieldRef<WearNTear, bool> WearNTearHaveRoofRef = AccessTools.FieldRefAccess<WearNTear, bool>("m_haveRoof");

	private static readonly FieldRef<WearNTear, bool> WearNTearHaveAshRoofRef = AccessTools.FieldRefAccess<WearNTear, bool>("m_haveAshRoof");

	private static readonly FieldRef<WearNTear, GameObject> WearNTearWetObjectRef = AccessTools.FieldRefAccess<WearNTear, GameObject>("m_wet");

	private Counts _counts = default(Counts);

	private ConfigEntry<bool> _enableProfiler;

	private ConfigEntry<bool> _enableConsoleLogging;

	private ConfigEntry<bool> _showProfilerOnStart;

	private ConfigEntry<bool> _enableAutomaticProfilerPolling;

	private ConfigEntry<float> _profilerPollInterval;

	private ConfigEntry<KeyCode> _toggleProfilerKey;

	private ConfigEntry<bool> _enableFireOptimizations;

	private ConfigEntry<bool> _enableWearNTearOptimizations;

	private ConfigEntry<float> _wearNTearBypassSupportValue;

	private ConfigEntry<string> _knownFirePieceNameTokens;

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

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

	private readonly HashSet<Piece> _fireCandidatePieces = new HashSet<Piece>();

	private readonly Stack<GameObject> _proxyLightPool = new Stack<GameObject>();

	private readonly HashSet<GameObject> _availableProxyLightObjects = new HashSet<GameObject>();

	private readonly HashSet<GameObject> _allProxyLightObjects = new HashSet<GameObject>();

	private readonly Dictionary<ClusterProxyKey, ClusterProxyLightState> _clusterProxyLights = new Dictionary<ClusterProxyKey, ClusterProxyLightState>();

	private readonly List<ClusterProxyKey> _inactiveClusterProxyKeys = new List<ClusterProxyKey>();

	private readonly RaycastHit[] _fireOcclusionHits = (RaycastHit[])(object)new RaycastHit[64];

	private readonly Plane[] _fireFrustumPlanes = (Plane[])(object)new Plane[6];

	private static readonly RaycastHitDistanceComparer FireOcclusionHitComparer = new RaycastHitDistanceComparer();

	private FireMetrics _fireMetrics = default(FireMetrics);

	private float _nextOptimizerUpdateTime;

	private float _nextFireCandidateRefreshTime;

	private int _nextFireOcclusionStartIndex;

	private int _fireOcclusionBudgetGeneration;

	private bool _hasRefreshedFireCandidates;

	private bool _fireCandidateRefreshRequested;

	private bool _fireOptimizationsWereActive;

	private GameObject _proxyLightPoolRoot;

	private string _cachedFireLightUpdateTokenConfig;

	private string[] _cachedFireLightUpdateTokens = new string[0];

	private string _cachedKnownFireNameTokenConfig;

	private string[] _cachedKnownFireNameTokens = new string[0];

	private ConfigEntry<float> _optimizerUpdateInterval;

	private ConfigEntry<float> _fireCandidateRefreshInterval;

	private ConfigEntry<bool> _enablePeriodicFireCandidateRefresh;

	private ConfigEntry<FireOptimizationMode> _fireOptimizationMode;

	private ConfigEntry<bool> _useFireVisibilityCulling;

	private ConfigEntry<float> _fireVisibilityGraceSeconds;

	private ConfigEntry<float> _staticLightIntensityMultiplier;

	private ConfigEntry<float> _staticLightRangeMultiplier;

	private ConfigEntry<string> _fireLightUpdateComponentNameTokens;

	private ConfigEntry<bool> _useClusteredFireProxyLights;

	private ConfigEntry<float> _clusteredFireProxyCellSize;

	private ConfigEntry<bool> _useFireOcclusionCulling;

	private ConfigEntry<bool> _ignoreKnownFirePiecesInFireOcclusion;

	private ConfigEntry<float> _fireOcclusionRayRadius;

	private ConfigEntry<float> _fireOcclusionCacheSeconds;

	private ConfigEntry<int> _maxFireOcclusionChecksPerUpdate;

	private ConfigEntry<bool> _debugFireOcclusion;

	private ConfigEntry<float> _fireRestoreGraceSeconds;

	private ConfigEntry<int> _topLightOffenderCount;

	private Texture2D _opaqueBackground;

	private const string ColliderClusterRootName = "BuildPieceProfiler_ColliderClusters";

	private const string ColliderClusterCellName = "ColliderClusterCell";

	private static readonly HashSet<string> ColliderClusterDeniedTypeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
	{
		"ArmorStand", "Aoe", "Container", "CookingStation", "CraftingStation", "Door", "EffectFade", "Fermenter", "Fireplace", "ItemStand",
		"ShieldGenerator", "Ship", "Sign", "Smelter", "SpinningWheel", "TeleportWorld", "Trap", "Turret", "Vagon", "Vine",
		"VortexParticles", "Windmill"
	};

	private readonly Dictionary<ColliderClusterCellKey, ColliderClusterCell> _colliderClusterCells = new Dictionary<ColliderClusterCellKey, ColliderClusterCell>();

	private readonly Dictionary<Piece, ColliderClusterCellKey> _colliderClusterPieceCells = new Dictionary<Piece, ColliderClusterCellKey>();

	private readonly Queue<ColliderClusterCellKey> _colliderClusterDirtyQueue = new Queue<ColliderClusterCellKey>();

	private ConfigEntry<bool> _enableColliderClusterSystem;

	private ConfigEntry<bool> _enableColliderClusterProfiling;

	private ConfigEntry<bool> _enableColliderClustering;

	private ConfigEntry<float> _colliderClusterCellSize;

	private ConfigEntry<int> _colliderClusterMinimumBoxes;

	private ConfigEntry<int> _colliderClusterMaximumBoxesPerCollider;

	private ConfigEntry<float> _colliderClusterRestoreDistance;

	private ConfigEntry<float> _colliderClusterActivationDistance;

	private ConfigEntry<float> _colliderClusterUpdateInterval;

	private ConfigEntry<int> _colliderClusterCellsRebuiltPerUpdate;

	private ConfigEntry<int> _colliderClusterDiscoveryPiecesPerUpdate;

	private ConfigEntry<string> _colliderClusterExcludedNameTokens;

	private GameObject _colliderClusterRoot;

	private Piece[] _colliderClusterDiscoveryPieces;

	private int _colliderClusterDiscoveryIndex;

	private bool _colliderClusterDiscoveryComplete;

	private bool _colliderClusteringWasActive;

	private int _colliderClusterSettingsSignature;

	private string _cachedColliderClusterExcludedTokenConfig;

	private string[] _cachedColliderClusterExcludedTokens = new string[0];

	private float _nextColliderClusterSettingsCheck;

	private float _nextColliderClusterDistanceUpdate;

	private float _nextColliderClusterMetricsRefresh;

	private ColliderClusterMetrics _colliderClusterMetrics;

	private static bool _networkProfilerHooksActive;

	private Harmony _networkProfilerHarmony;

	private bool _networkProfilerHooksInstalled;

	private readonly Dictionary<ZDOID, NetworkPieceState> _networkPieceStatesByZdo = new Dictionary<ZDOID, NetworkPieceState>();

	private readonly Dictionary<ZNetView, NetworkPieceState> _networkPieceStatesByView = new Dictionary<ZNetView, NetworkPieceState>();

	private readonly Dictionary<string, int> _networkRpcMethodCounts = new Dictionary<string, int>(StringComparer.Ordinal);

	private readonly List<NetworkPieceState> _networkPieceStateList = new List<NetworkPieceState>();

	private readonly List<NetworkWriterSnapshot> _networkTopWriters = new List<NetworkWriterSnapshot>();

	private readonly List<NetworkRpcSnapshot> _networkTopRpcMethods = new List<NetworkRpcSnapshot>();

	private readonly List<NetworkWriterSnapshot> _networkWriterAggregation = new List<NetworkWriterSnapshot>();

	private ConfigEntry<bool> _enableNetworkIdleSystem;

	private ConfigEntry<bool> _enableNetworkIdleProfiling;

	private ConfigEntry<int> _networkDiscoveryPiecesPerUpdate;

	private ConfigEntry<int> _networkRevisionSamplesPerUpdate;

	private ConfigEntry<float> _networkMetricsInterval;

	private ConfigEntry<float> _networkActiveWriterSeconds;

	private ConfigEntry<int> _networkTopWriterCount;

	private Piece[] _networkDiscoveryPieces;

	private int _networkDiscoveryIndex;

	private bool _networkDiscoveryComplete;

	private bool _networkProfilerWorldActive;

	private int _networkRevisionSampleCursor;

	private float _nextNetworkMetricsRefresh;

	private int _networkMetricsAggregationCursor;

	private NetworkMetricsAggregate _networkMetricsAggregate;

	private long _networkWriteAttempts;

	private long _networkChangedWrites;

	private long _networkRpcCalls;

	private long _networkObservedRevisionChanges;

	private long _networkObservedOwnerChanges;

	private NetworkIdleMetrics _networkIdleMetrics;

	private const string RendererBatchRootName = "BuildPieceProfiler_RendererBatches";

	private const string RendererBatchCellName = "RendererBatchCell";

	private const string RendererBatchObjectName = "RendererBatch";

	private const string RendererShadowBatchObjectName = "RendererShadowBatch";

	private readonly Dictionary<RendererBatchCellKey, RendererBatchCell> _rendererBatchCells = new Dictionary<RendererBatchCellKey, RendererBatchCell>();

	private readonly Dictionary<Piece, RendererBatchCellKey> _rendererBatchPieceCells = new Dictionary<Piece, RendererBatchCellKey>();

	private readonly Queue<RendererBatchCellKey> _rendererBatchDirtyQueue = new Queue<RendererBatchCellKey>();

	private readonly Dictionary<Piece, float> _rendererBatchPieceExcludedUntil = new Dictionary<Piece, float>();

	private readonly Dictionary<Mesh, RendererShadowSourceGeometry> _rendererShadowGeometryCache = new Dictionary<Mesh, RendererShadowSourceGeometry>();

	private readonly Dictionary<Piece, RendererBatchPieceEligibility> _rendererBatchEligibilityCache = new Dictionary<Piece, RendererBatchPieceEligibility>();

	private readonly Dictionary<Piece, MeshRenderer[]> _rendererBatchRendererCache = new Dictionary<Piece, MeshRenderer[]>();

	private readonly Dictionary<MeshRenderer, RendererLodCacheEntry> _rendererLodCache = new Dictionary<MeshRenderer, RendererLodCacheEntry>();

	private readonly Dictionary<Piece, float> _rendererBatchHighlightedUntil = new Dictionary<Piece, float>();

	private ConfigEntry<bool> _enableRendererBatching;

	private ConfigEntry<bool> _enableShadowOptimizationSystem;

	private ConfigEntry<float> _rendererBatchCellSize;

	private ConfigEntry<int> _rendererBatchMinimumRenderers;

	private ConfigEntry<int> _rendererBatchMaximumVertices;

	private ConfigEntry<int> _rendererBatchCellsPerUpdate;

	private ConfigEntry<int> _rendererBatchDiscoveryPiecesPerUpdate;

	private ConfigEntry<float> _rendererBatchRebuildDelay;

	private ConfigEntry<string> _rendererBatchExcludedNameTokens;

	private ConfigEntry<bool> _enableShadowCasterOptimization;

	private ConfigEntry<int> _shadowClusterMinimumVisibleBatches;

	private ConfigEntry<int> _shadowClusterMinimumDrawsSaved;

	private ConfigEntry<int> _shadowClusterMaximumVertices;

	private ConfigEntry<int> _shadowClusterMaximumVerticesPerDrawSaved;

	private ConfigEntry<float> _shadowClusterMaximumBoundsDiagonal;

	private ConfigEntry<bool> _enableSimplifiedStructuralShadowCasters;

	private ConfigEntry<string> _simplifiedShadowPieceNameTokens;

	private GameObject _rendererBatchRoot;

	private Piece[] _rendererBatchDiscoveryPieces;

	private int _rendererBatchDiscoveryIndex;

	private bool _rendererBatchDiscoveryComplete;

	private bool _rendererBatchingWasActive;

	private int _rendererBatchSettingsSignature;

	private string _cachedRendererBatchExcludedTokenConfig;

	private string[] _cachedRendererBatchExcludedTokens = new string[0];

	private string _cachedSimplifiedShadowTokenConfig;

	private string[] _cachedSimplifiedShadowTokens = new string[0];

	private float _nextRendererBatchSettingsCheck;

	private float _nextRendererBatchMetricsRefresh;

	private RendererBatchMetrics _rendererBatchMetrics;

	private static readonly HashSet<string> StaticSleepGameplayTypeDenylist = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
	{
		"ArmorStand", "Catapult", "ConditionalObject", "Container", "CookingStation", "CraftingStation", "Door", "Fermenter", "Fireplace", "ItemStand",
		"Piece", "RandomAnimation", "ShieldGenerator", "Ship", "SiegeMachine", "Sign", "Smelter", "SpinningWheel", "TeleportWorld", "Trap",
		"Turret", "Vagon", "Vine", "WearNTear", "Windmill", "ZNetView", "ZSyncTransform"
	};

	private static readonly string[] StaticSleepUpdateMethodNames = new string[3] { "Update", "LateUpdate", "FixedUpdate" };

	private readonly Dictionary<Piece, StaticSleepPieceState> _staticSleepPieceStates = new Dictionary<Piece, StaticSleepPieceState>();

	private readonly HashSet<Piece> _staticSleepProfiledPieces = new HashSet<Piece>();

	private readonly Dictionary<Piece, List<StaticSleepProfileRecord>> _staticSleepPieceProfiles = new Dictionary<Piece, List<StaticSleepProfileRecord>>();

	private readonly Dictionary<Type, StaticSleepTypeMetrics> _staticSleepTypeMetrics = new Dictionary<Type, StaticSleepTypeMetrics>();

	private readonly Dictionary<Type, bool> _staticSleepUpdateLoopCache = new Dictionary<Type, bool>();

	private readonly List<StaticSleepScheduleEntry> _staticSleepScheduleHeap = new List<StaticSleepScheduleEntry>();

	private readonly List<StaticSleepTypeSnapshot> _staticSleepTopTypeSnapshot = new List<StaticSleepTypeSnapshot>();

	private ConfigEntry<bool> _enableStaticComponentSystem;

	private ConfigEntry<bool> _enableStaticComponentProfiling;

	private ConfigEntry<bool> _enableStaticComponentSleeping;

	private ConfigEntry<string> _staticSleepComponentTypeNames;

	private ConfigEntry<int> _staticSleepDiscoveryPiecesPerUpdate;

	private ConfigEntry<float> _staticSleepWakeGraceSeconds;

	private ConfigEntry<int> _staticSleepTopTypeCount;

	private Piece[] _staticSleepDiscoveryPieces;

	private int _staticSleepDiscoveryIndex;

	private bool _staticSleepDiscoveryComplete;

	private bool _staticSleepWorldActive;

	private int _staticSleepSettingsSignature;

	private string _cachedStaticSleepTypeConfig;

	private HashSet<string> _cachedStaticSleepTypeNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

	private float _nextStaticSleepSettingsCheck;

	private float _nextStaticSleepMetricsRefresh;

	private int _staticSleepCandidatePieceCount;

	private StaticSleepMetrics _staticSleepMetrics;

	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 HasActiveFireVisuals(Light[] lights, ParticleSystem[] particles)
	{
		if (lights != null)
		{
			foreach (Light val in lights)
			{
				if ((Object)(object)val != (Object)null && ((Behaviour)val).enabled && ((Component)val).gameObject.activeInHierarchy)
				{
					return true;
				}
			}
		}
		if (particles != null)
		{
			foreach (ParticleSystem val2 in particles)
			{
				if ((Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy && (val2.isPlaying || val2.IsAlive(true)))
				{
					return true;
				}
			}
		}
		return false;
	}

	private string NormalizeNameToken(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		return value.ToLowerInvariant().Replace("(clone)", string.Empty).Trim();
	}

	private string[] GetNameTokens(string configValue)
	{
		if (string.IsNullOrEmpty(configValue))
		{
			return new string[0];
		}
		string[] array = configValue.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
		List<string> list = new List<string>();
		string[] array2 = array;
		foreach (string value in array2)
		{
			string text = NormalizeNameToken(value);
			if (text.Length > 0)
			{
				list.Add(text);
			}
		}
		return list.ToArray();
	}

	private string[] GetCachedFireLightUpdateNameTokens()
	{
		string text = ((_fireLightUpdateComponentNameTokens != null) ? _fireLightUpdateComponentNameTokens.Value : string.Empty);
		if (_cachedFireLightUpdateTokenConfig != text)
		{
			_cachedFireLightUpdateTokenConfig = text;
			_cachedFireLightUpdateTokens = GetNameTokens(text);
		}
		return _cachedFireLightUpdateTokens;
	}

	private string[] GetCachedKnownFireNameTokens()
	{
		string text = ((_knownFirePieceNameTokens != null) ? _knownFirePieceNameTokens.Value : string.Empty);
		if (_cachedKnownFireNameTokenConfig != text)
		{
			_cachedKnownFireNameTokenConfig = text;
			_cachedKnownFireNameTokens = GetNameTokens(text);
		}
		return _cachedKnownFireNameTokens;
	}

	private bool IsKnownFirePiece(Piece piece, string[] knownFireNameTokens)
	{
		if ((Object)(object)piece == (Object)null || knownFireNameTokens == null || knownFireNameTokens.Length == 0)
		{
			return false;
		}
		string text = NormalizeNameToken(((Object)(object)((Component)piece).gameObject != (Object)null) ? ((Object)((Component)piece).gameObject).name : ((Object)piece).name);
		string text2 = NormalizeNameToken(((Object)piece).name);
		foreach (string value in knownFireNameTokens)
		{
			if (text.Contains(value) || text2.Contains(value))
			{
				return true;
			}
		}
		return false;
	}

	private bool IsPieceVisible(MeshRenderer[] renderers, Plane[] frustumPlanes)
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		if (renderers == null)
		{
			return false;
		}
		foreach (MeshRenderer val in renderers)
		{
			if (!((Object)(object)val == (Object)null) && ((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy && ((frustumPlanes != null) ? GeometryUtility.TestPlanesAABB(frustumPlanes, ((Renderer)val).bounds) : ((Renderer)val).isVisible))
			{
				return true;
			}
		}
		return false;
	}

	private bool ShouldRefreshProfilerMetrics()
	{
		return _showOverlay || (_enableConsoleLogging != null && _enableConsoleLogging.Value);
	}

	private void EnforceOptimizedFireState()
	{
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		foreach (FireOptimizationState value in _fireStates.Values)
		{
			if (value == null || (Object)(object)value.Piece == (Object)null)
			{
				continue;
			}
			foreach (KeyValuePair<Light, bool> item in value.OriginalLightEnabled)
			{
				Light key = item.Key;
				if (!((Object)(object)key == (Object)null) && !IsOurProxyLight(key))
				{
					if (((Behaviour)key).enabled)
					{
						((Behaviour)key).enabled = false;
					}
					if (key.cullingMask != 0)
					{
						key.cullingMask = 0;
					}
				}
			}
			if (value.AppliedMode == AppliedFireMode.FullCull)
			{
				DisableProxyLight(value);
			}
			foreach (KeyValuePair<Behaviour, bool> item2 in value.OriginalBehaviourEnabled)
			{
				if ((Object)(object)item2.Key != (Object)null && item2.Key.enabled)
				{
					item2.Key.enabled = false;
				}
			}
			foreach (ParticleSystem forcedStoppedParticle in value.ForcedStoppedParticles)
			{
				if (!((Object)(object)forcedStoppedParticle == (Object)null))
				{
					EmissionModule emission = forcedStoppedParticle.emission;
					if (((EmissionModule)(ref emission)).enabled)
					{
						((EmissionModule)(ref emission)).enabled = false;
					}
					if (forcedStoppedParticle.isPlaying)
					{
						forcedStoppedParticle.Stop(true, (ParticleSystemStopBehavior)0);
					}
				}
			}
		}
	}

	private void RestoreFire(Piece piece)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: 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<MeshRenderer, ShadowCastingMode> originalShadowMode in value.OriginalShadowModes)
		{
			if ((Object)(object)originalShadowMode.Key != (Object)null)
			{
				((Renderer)originalShadowMode.Key).shadowCastingMode = originalShadowMode.Value;
			}
		}
		foreach (KeyValuePair<Behaviour, bool> item in value.OriginalBehaviourEnabled)
		{
			if ((Object)(object)item.Key != (Object)null)
			{
				item.Key.enabled = item.Value;
			}
		}
		bool flag = (Object)(object)value.Source == (Object)null || value.Source.IsBurning();
		foreach (KeyValuePair<Light, bool> item2 in value.OriginalLightEnabled)
		{
			if ((Object)(object)item2.Key != (Object)null)
			{
				if (value.OriginalLightCullingMask.TryGetValue(item2.Key, out var value2))
				{
					item2.Key.cullingMask = value2;
				}
				((Behaviour)item2.Key).enabled = flag && item2.Value;
			}
		}
		foreach (KeyValuePair<ParticleSystem, bool> item3 in value.OriginalParticlePlaying)
		{
			ParticleSystem key = item3.Key;
			if (!((Object)(object)key == (Object)null))
			{
				if (value.OriginalParticleEmissionEnabled.TryGetValue(key, out var value3))
				{
					EmissionModule emission = key.emission;
					((EmissionModule)(ref emission)).enabled = value3;
				}
				if (flag && item3.Value)
				{
					PlayParticle(key);
				}
				else if (key.isPlaying)
				{
					key.Stop(true, (ParticleSystemStopBehavior)0);
				}
			}
		}
		DisableProxyLight(value);
		_fireStates.Remove(piece);
	}

	private void DisableFireLightUpdateBehaviours(Piece piece, FireOptimizationState state)
	{
		if ((Object)(object)piece == (Object)null || state == null || _fireLightUpdateComponentNameTokens == null)
		{
			return;
		}
		string[] cachedFireLightUpdateNameTokens = GetCachedFireLightUpdateNameTokens();
		if (cachedFireLightUpdateNameTokens.Length == 0)
		{
			return;
		}
		MonoBehaviour[] componentsInChildren = ((Component)piece).GetComponentsInChildren<MonoBehaviour>(true);
		MonoBehaviour[] array = componentsInChildren;
		foreach (MonoBehaviour val in array)
		{
			if (!((Object)(object)val == (Object)null) && !state.OriginalBehaviourEnabled.ContainsKey((Behaviour)(object)val) && IsFireLightUpdateBehaviour(val, cachedFireLightUpdateNameTokens))
			{
				state.OriginalBehaviourEnabled[(Behaviour)(object)val] = ((Behaviour)val).enabled;
				if (((Behaviour)val).enabled)
				{
					((Behaviour)val).enabled = false;
				}
			}
		}
	}

	private bool IsFireLightUpdateBehaviour(MonoBehaviour behaviour, string[] tokens)
	{
		if ((Object)(object)behaviour == (Object)null || tokens == null || tokens.Length == 0)
		{
			return false;
		}
		Type type = ((object)behaviour).GetType();
		string text = NormalizeNameToken(type.Name);
		string text2 = NormalizeNameToken(type.FullName);
		foreach (string value in tokens)
		{
			if (text.Contains(value) || text2.Contains(value))
			{
				return true;
			}
		}
		return false;
	}

	private int CountFireLightUpdateBehaviours(Piece piece)
	{
		if ((Object)(object)piece == (Object)null || _fireLightUpdateComponentNameTokens == null)
		{
			return 0;
		}
		string[] cachedFireLightUpdateNameTokens = GetCachedFireLightUpdateNameTokens();
		if (cachedFireLightUpdateNameTokens.Length == 0)
		{
			return 0;
		}
		int num = 0;
		MonoBehaviour[] componentsInChildren = ((Component)piece).GetComponentsInChildren<MonoBehaviour>(true);
		MonoBehaviour[] array = componentsInChildren;
		foreach (MonoBehaviour val in array)
		{
			if ((Object)(object)val != (Object)null && ((Behaviour)val).enabled && IsFireLightUpdateBehaviour(val, cachedFireLightUpdateNameTokens))
			{
				num++;
			}
		}
		return num;
	}

	private void RestoreAllFireOptimizations()
	{
		List<KeyValuePair<Piece, FireOptimizationState>> list = new List<KeyValuePair<Piece, FireOptimizationState>>(_fireStates);
		foreach (KeyValuePair<Piece, FireOptimizationState> item in list)
		{
			if ((Object)(object)item.Key != (Object)null)
			{
				RestoreFire(item.Key);
				continue;
			}
			if (item.Value != null)
			{
				ReleaseProxyLightObject(item.Value.ProxyLightObject);
			}
			_fireStates.Remove(item.Key);
		}
		ReleaseAllClusterProxyLights();
	}

	private bool HasActiveFireOptimizations()
	{
		return _fireStates.Count > 0;
	}

	private void ResetFireCandidateCache()
	{
		_fireCandidates.Clear();
		_fireCandidatePieces.Clear();
		_nextFireCandidateRefreshTime = 0f;
		_nextFireOcclusionStartIndex = 0;
		_hasRefreshedFireCandidates = false;
		_fireCandidateRefreshRequested = false;
		_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)
		{
			if (item.Value != null)
			{
				ReleaseProxyLightObject(item.Value.ProxyLightObject);
			}
			_fireStates.Remove(item.Key);
		}
	}

	private void RefreshFireCandidateCache()
	{
		_hasRefreshedFireCandidates = true;
		_fireCandidateRefreshRequested = false;
		Dictionary<Piece, FireCandidate> dictionary = new Dictionary<Piece, FireCandidate>();
		foreach (FireCandidate fireCandidate2 in _fireCandidates)
		{
			if (fireCandidate2 != null && (Object)(object)fireCandidate2.Piece != (Object)null)
			{
				dictionary[fireCandidate2.Piece] = fireCandidate2;
			}
		}
		_fireCandidates.Clear();
		_fireCandidatePieces.Clear();
		Fireplace[] array = Object.FindObjectsByType<Fireplace>((FindObjectsSortMode)0);
		string[] cachedKnownFireNameTokens = GetCachedKnownFireNameTokens();
		float time = Time.time;
		Fireplace[] array2 = array;
		foreach (Fireplace val in array2)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			Piece componentInParent = ((Component)val).GetComponentInParent<Piece>();
			if ((Object)(object)componentInParent == (Object)null || !((Component)componentInParent).gameObject.activeInHierarchy || _fireCandidatePieces.Contains(componentInParent) || !IsKnownFirePiece(componentInParent, cachedKnownFireNameTokens))
			{
				continue;
			}
			Light[] originalLights = GetOriginalLights(((Component)componentInParent).GetComponentsInChildren<Light>(true));
			if (originalLights.Length == 0)
			{
				continue;
			}
			ParticleSystem[] componentsInChildren = ((Component)componentInParent).GetComponentsInChildren<ParticleSystem>(true);
			if (IsFireCandidate(originalLights, componentsInChildren))
			{
				FireCandidate fireCandidate = new FireCandidate
				{
					Piece = componentInParent,
					Source = val,
					Renderers = ((Component)componentInParent).GetComponentsInChildren<MeshRenderer>(true),
					OriginalLights = originalLights,
					Particles = componentsInChildren,
					LastRelevantTime = time,
					LastIrrelevantTime = time
				};
				if (dictionary.TryGetValue(componentInParent, out var value))
				{
					fireCandidate.HasOcclusionResult = value.HasOcclusionResult;
					fireCandidate.CachedOccluded = value.CachedOccluded;
					fireCandidate.LastOcclusionCheckTime = value.LastOcclusionCheckTime;
					fireCandidate.LastRelevantTime = value.LastRelevantTime;
					fireCandidate.LastIrrelevantTime = value.LastIrrelevantTime;
				}
				_fireCandidates.Add(fireCandidate);
				_fireCandidatePieces.Add(componentInParent);
			}
		}
		List<Piece> list = null;
		foreach (KeyValuePair<Piece, FireOptimizationState> fireState in _fireStates)
		{
			Piece key = fireState.Key;
			FireOptimizationState value2 = fireState.Value;
			if (!((Object)(object)key == (Object)null) && value2 != null && !_fireCandidatePieces.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()
	{
		ResetNetworkIdleProfiling();
		RestoreAllColliderClusters();
		ResetStaticComponentSleeping(clearProfile: true);
		RestoreAllRendererBatches();
		RestoreAllFireOptimizations();
		DestroyProxyLightPool();
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		_loadedPieceRegistry.Clear();
		_loadedPieceSnapshot = null;
		_loadedPieceRegistryInitialized = false;
		if ((Object)(object)_opaqueBackground != (Object)null)
		{
			Object.Destroy((Object)(object)_opaqueBackground);
			_opaqueBackground = null;
		}
		if ((Object)(object)_instance == (Object)(object)this)
		{
			_instance = null;
		}
		_wearNTearOptimizationActive = false;
		_cachedBypassedSupportValue = 1000000f;
	}

	private void EnsureLoadedPieceRegistry()
	{
		if (_loadedPieceRegistryInitialized)
		{
			return;
		}
		Piece[] array;
		try
		{
			List<Piece> list = ((PieceAllPiecesField != null) ? (PieceAllPiecesField.GetValue(null) as List<Piece>) : null);
			array = ((list != null) ? list.ToArray() : Object.FindObjectsByType<Piece>((FindObjectsSortMode)0));
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)("Piece registry snapshot unavailable: " + ex.Message));
			array = Object.FindObjectsByType<Piece>((FindObjectsSortMode)0);
		}
		Piece[] array2 = array;
		foreach (Piece val in array2)
		{
			if ((Object)(object)val != (Object)null)
			{
				_loadedPieceRegistry.Add(val);
			}
		}
		_loadedPieceRegistryInitialized = true;
		_loadedPieceSnapshotDirty = true;
	}

	private Piece[] GetSharedLoadedPiecesSnapshot()
	{
		EnsureLoadedPieceRegistry();
		if (_loadedPieceSnapshotDirty || _loadedPieceSnapshot == null)
		{
			_loadedPieceRegistry.RemoveWhere((Piece piece) => (Object)(object)piece == (Object)null);
			_loadedPieceSnapshot = (Piece[])(object)new Piece[_loadedPieceRegistry.Count];
			_loadedPieceRegistry.CopyTo(_loadedPieceSnapshot);
			_loadedPieceSnapshotDirty = false;
		}
		return _loadedPieceSnapshot;
	}

	private void RegisterLoadedPiece(Piece piece)
	{
		if ((Object)(object)piece != (Object)null && _loadedPieceRegistry.Add(piece))
		{
			_loadedPieceSnapshotDirty = true;
		}
	}

	private void UnregisterLoadedPiece(Piece piece)
	{
		if (piece != null && _loadedPieceRegistry.Remove(piece))
		{
			_loadedPieceSnapshotDirty = true;
		}
	}

	private static void NotifyPieceLoadedOrChanged(Piece piece, bool forceRefresh)
	{
		BuildPieceProfilerPlugin instance = _instance;
		if (!((Object)(object)instance == (Object)null) && !((Object)(object)piece == (Object)null))
		{
			instance.RegisterLoadedPiece(piece);
			instance.NotifyRendererBatchPieceChanged(piece, forceRefresh);
			instance.NotifyColliderClusterPieceChanged(piece, forceRefresh);
			instance.NotifyStaticSleepPieceChanged(piece);
			instance.NotifyNetworkPieceChanged(piece);
		}
	}

	private static void NotifyPieceDestroyed(Piece piece)
	{
		BuildPieceProfilerPlugin instance = _instance;
		if (!((Object)(object)instance == (Object)null))
		{
			instance.NotifyRendererBatchPieceDestroyed(piece);
			instance.UntrackColliderClusterPiece(piece);
			instance.UntrackStaticSleepPiece(piece);
			instance.UntrackNetworkPiece(piece);
			instance.UnregisterLoadedPiece(piece);
		}
	}

	private static bool IsWearNTearOptimizationEnabled()
	{
		return _wearNTearOptimizationActive;
	}

	private static float GetBypassedSupportValue()
	{
		return _cachedBypassedSupportValue;
	}

	private static void ApplyBypassedSupport(WearNTear wearNTear)
	{
		if (!((Object)(object)wearNTear == (Object)null))
		{
			WearNTearSupportRef.Invoke(wearNTear) = GetBypassedSupportValue();
		}
	}

	private static void ApplyDryRoofedState(WearNTear wearNTear)
	{
		if (!((Object)(object)wearNTear == (Object)null))
		{
			WearNTearRainWetRef.Invoke(wearNTear) = false;
			WearNTearHaveRoofRef.Invoke(wearNTear) = true;
			WearNTearHaveAshRoofRef.Invoke(wearNTear) = true;
			GameObject val = WearNTearWetObjectRef.Invoke(wearNTear);
			if ((Object)(object)val != (Object)null && val.activeSelf)
			{
				val.SetActive(false);
			}
		}
	}

	private void ApplyStaticLight(Piece piece, Fireplace source, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		FireOptimizationState fireOptimizationState = PrepareFireState(piece, source, AppliedFireMode.StaticLight, renderers, lights, particles);
		fireOptimizationState.ForcedStoppedParticles.Clear();
		Light firstOriginallyEnabledLight = GetFirstOriginallyEnabledLight(fireOptimizationState, lights);
		if ((Object)(object)firstOriginallyEnabledLight == (Object)null)
		{
			DisableProxyLight(fireOptimizationState);
		}
		else if (_useClusteredFireProxyLights.Value)
		{
			DisableProxyLight(fireOptimizationState);
			EnsureClusterProxyLight(piece, firstOriginallyEnabledLight);
		}
		else
		{
			EnsureProxyLight(piece, fireOptimizationState, firstOriginallyEnabledLight);
		}
		DisableOriginalLights(lights);
		AddForcedStoppedParticles(fireOptimizationState, particles);
		DisableParticleEmission(particles);
		StopParticles(particles);
		DisableShadows(renderers);
	}

	private void AddForcedStoppedParticles(FireOptimizationState state, ParticleSystem[] particles)
	{
		if (state == null || particles == null)
		{
			return;
		}
		foreach (ParticleSystem val in particles)
		{
			if ((Object)(object)val != (Object)null)
			{
				state.ForcedStoppedParticles.Add(val);
			}
		}
	}

	private void ApplyFullCull(Piece piece, Fireplace source, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		FireOptimizationState fireOptimizationState = PrepareFireState(piece, source, AppliedFireMode.FullCull, renderers, lights, particles);
		fireOptimizationState.ForcedStoppedParticles.Clear();
		DisableProxyLight(fireOptimizationState);
		DisableOriginalLights(lights);
		AddForcedStoppedParticles(fireOptimizationState, particles);
		DisableParticleEmission(particles);
		StopParticles(particles);
		DisableShadows(renderers);
	}

	private FireOptimizationState PrepareFireState(Piece piece, Fireplace source, AppliedFireMode mode, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		if (_fireStates.TryGetValue(piece, out var value) && value.AppliedMode != mode)
		{
			RestoreFire(piece);
			value = null;
		}
		if (value == null)
		{
			value = new FireOptimizationState
			{
				Piece = piece,
				Source = source,
				AppliedMode = mode
			};
			_fireStates[piece] = value;
		}
		else
		{
			value.Source = source;
		}
		StoreOriginalStates(value, renderers, lights, particles);
		DisableFireLightUpdateBehaviours(piece, value);
		return value;
	}

	private void StoreOriginalStates(FireOptimizationState state, MeshRenderer[] renderers, Light[] lights, ParticleSystem[] particles)
	{
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		if (state == null)
		{
			return;
		}
		lights = (Light[])(((object)lights) ?? ((object)new Light[0]));
		particles = (ParticleSystem[])(((object)particles) ?? ((object)new ParticleSystem[0]));
		renderers = (MeshRenderer[])(((object)renderers) ?? ((object)new MeshRenderer[0]));
		Light[] array = lights;
		foreach (Light val in array)
		{
			if (!((Object)(object)val == (Object)null) && !state.OriginalLightEnabled.ContainsKey(val))
			{
				state.OriginalLightEnabled[val] = ((Behaviour)val).enabled;
				state.OriginalLightCullingMask[val] = val.cullingMask;
			}
		}
		ParticleSystem[] array2 = particles;
		foreach (ParticleSystem val2 in array2)
		{
			if (!((Object)(object)val2 == (Object)null) && !state.OriginalParticlePlaying.ContainsKey(val2))
			{
				state.OriginalParticlePlaying[val2] = val2.isPlaying;
				EmissionModule emission = val2.emission;
				state.OriginalParticleEmissionEnabled[val2] = ((EmissionModule)(ref emission)).enabled;
			}
		}
		MeshRenderer[] array3 = renderers;
		foreach (MeshRenderer val3 in array3)
		{
			if (!((Object)(object)val3 == (Object)null) && !state.OriginalShadowModes.ContainsKey(val3))
			{
				state.OriginalShadowModes[val3] = ((Renderer)val3).shadowCastingMode;
			}
		}
	}

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

	private void DestroyProxyLightPool()
	{
		foreach (FireOptimizationState value in _fireStates.Values)
		{
			if (value != null && (Object)(object)value.ProxyLightObject != (Object)null)
			{
				value.ProxyLightObject = null;
				value.ProxyLight = null;
			}
		}
		foreach (ClusterProxyLightState value2 in _clusterProxyLights.Values)
		{
			if (value2 != null && (Object)(object)value2.ProxyLightObject != (Object)null)
			{
				value2.ProxyLightObject = null;
				value2.ProxyLight = null;
			}
		}
		_clusterProxyLights.Clear();
		foreach (GameObject allProxyLightObject in _allProxyLightObjects)
		{
			if ((Object)(object)allProxyLightObject != (Object)null)
			{
				Object.Destroy((Object)(object)allProxyLightObject);
			}
		}
		_proxyLightPool.Clear();
		_availableProxyLightObjects.Clear();
		_allProxyLightObjects.Clear();
		if ((Object)(object)_proxyLightPoolRoot != (Object)null)
		{
			Object.Destroy((Object)(object)_proxyLightPoolRoot);
			_proxyLightPoolRoot = null;
		}
	}

	private void PlayParticle(ParticleSystem particle)
	{
		if (!((Object)(object)particle == (Object)null) && ((Component)particle).gameObject.activeInHierarchy && !particle.isPlaying)
		{
			particle.Play(true);
		}
	}

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

	private void DisableParticleEmission(ParticleSystem[] particles)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (particles == null)
		{
			return;
		}
		foreach (ParticleSystem val in particles)
		{
			if (!((Object)(object)val == (Object)null))
			{
				EmissionModule emission = val.emission;
				((EmissionModule)(ref emission)).enabled = false;
			}
		}
	}

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

	private Light GetFirstOriginallyEnabledLight(FireOptimizationState state, Light[] lights)
	{
		if (state == null || lights == null)
		{
			return null;
		}
		bool value = default(bool);
		foreach (Light val in lights)
		{
			if ((Object)(object)val != (Object)null && state.OriginalLightEnabled.TryGetValue(val, out value) && value)
			{
				return val;
			}
		}
		return null;
	}

	private GameObject GetProxyLightPoolRoot()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Expected O, but got Unknown
		if ((Object)(object)_proxyLightPoolRoot == (Object)null)
		{
			_proxyLightPoolRoot = new GameObject("BuildPieceProfiler_ProxyLightPool");
			_proxyLightPoolRoot.SetActive(true);
		}
		return _proxyLightPoolRoot;
	}

	private GameObject AcquireProxyLight(out Light proxyLight)
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Expected O, but got Unknown
		GameObject val = null;
		while (_proxyLightPool.Count > 0 && (Object)(object)val == (Object)null)
		{
			val = _proxyLightPool.Pop();
			_availableProxyLightObjects.Remove(val);
		}
		if ((Object)(object)val == (Object)null)
		{
			val = new GameObject("BuildPieceProfiler_StaticFireLightProxy");
			proxyLight = val.AddComponent<Light>();
			_allProxyLightObjects.Add(val);
		}
		else
		{
			proxyLight = val.GetComponent<Light>();
			if ((Object)(object)proxyLight == (Object)null)
			{
				proxyLight = val.AddComponent<Light>();
			}
		}
		((Object)val).name = "BuildPieceProfiler_StaticFireLightProxy";
		val.transform.SetParent((Transform)null, false);
		val.SetActive(true);
		return val;
	}

	private void ReleaseProxyLightObject(GameObject proxyObject)
	{
		if (!((Object)(object)proxyObject == (Object)null))
		{
			Light component = proxyObject.GetComponent<Light>();
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = false;
			}
			proxyObject.SetActive(false);
			proxyObject.transform.SetParent(GetProxyLightPoolRoot().transform, false);
			if (!_availableProxyLightObjects.Contains(proxyObject))
			{
				_availableProxyLightObjects.Add(proxyObject);
				_allProxyLightObjects.Add(proxyObject);
				_proxyLightPool.Push(proxyObject);
			}
		}
	}

	private void BeginClusterProxyLightUpdate()
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		if (!_useClusteredFireProxyLights.Value)
		{
			ReleaseAllClusterProxyLights();
			return;
		}
		foreach (ClusterProxyLightState value in _clusterProxyLights.Values)
		{
			if (value != null)
			{
				value.ActiveThisUpdate = false;
				value.MemberCount = 0;
				value.PositionSum = Vector3.zero;
				value.ColorSum = Color.black;
				value.IntensitySum = 0f;
				value.RangeMax = 0f;
				value.HasRotation = false;
			}
		}
	}

	private void FinalizeClusterProxyLightUpdate()
	{
		if (_clusterProxyLights.Count == 0)
		{
			return;
		}
		_inactiveClusterProxyKeys.Clear();
		foreach (KeyValuePair<ClusterProxyKey, ClusterProxyLightState> clusterProxyLight in _clusterProxyLights)
		{
			ClusterProxyLightState value = clusterProxyLight.Value;
			if (value != null)
			{
				if (value.ActiveThisUpdate)
				{
					UpdateClusterProxyLight(value);
				}
				else
				{
					_inactiveClusterProxyKeys.Add(clusterProxyLight.Key);
				}
			}
		}
		if (_inactiveClusterProxyKeys.Count == 0)
		{
			return;
		}
		foreach (ClusterProxyKey inactiveClusterProxyKey in _inactiveClusterProxyKeys)
		{
			if (_clusterProxyLights.TryGetValue(inactiveClusterProxyKey, out var value2))
			{
				ReleaseProxyLightObject(value2.ProxyLightObject);
				_clusterProxyLights.Remove(inactiveClusterProxyKey);
			}
		}
	}

	private void ReleaseAllClusterProxyLights()
	{
		if (_clusterProxyLights.Count == 0)
		{
			return;
		}
		foreach (ClusterProxyLightState value in _clusterProxyLights.Values)
		{
			if (value != null)
			{
				ReleaseProxyLightObject(value.ProxyLightObject);
			}
		}
		_clusterProxyLights.Clear();
	}

	private void EnsureClusterProxyLight(Piece piece, Light sourceLight)
	{
		//IL_003c: 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_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)piece == (Object)null) && !((Object)(object)sourceLight == (Object)null))
		{
			float cellSize = Mathf.Max(1f, _clusteredFireProxyCellSize.Value);
			Vector3 position = ((Component)sourceLight).transform.position;
			ClusterProxyKey clusterProxyKey = GetClusterProxyKey(position, cellSize);
			if (!_clusterProxyLights.TryGetValue(clusterProxyKey, out var value))
			{
				value = new ClusterProxyLightState();
				value.ProxyLightObject = AcquireProxyLight(out var proxyLight);
				value.ProxyLight = proxyLight;
				_clusterProxyLights[clusterProxyKey] = value;
			}
			else if ((Object)(object)value.ProxyLightObject == (Object)null || (Object)(object)value.ProxyLight == (Object)null)
			{
				value.ProxyLightObject = AcquireProxyLight(out var proxyLight2);
				value.ProxyLight = proxyLight2;
			}
			value.ActiveThisUpdate = true;
			value.MemberCount++;
			ClusterProxyLightState clusterProxyLightState = value;
			clusterProxyLightState.PositionSum += position;
			ClusterProxyLightState clusterProxyLightState2 = value;
			clusterProxyLightState2.ColorSum += sourceLight.color;
			value.IntensitySum += sourceLight.intensity;
			value.RangeMax = Mathf.Max(value.RangeMax, sourceLight.range);
			if (!value.HasRotation)
			{
				value.Rotation = ((Component)sourceLight).transform.rotation;
				value.HasRotation = true;
			}
			_inactiveClusterProxyKeys.Clear();
		}
	}

	private ClusterProxyKey GetClusterProxyKey(Vector3 position, float cellSize)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		return new ClusterProxyKey
		{
			X = Mathf.FloorToInt(position.x / cellSize),
			Y = Mathf.FloorToInt(position.y / cellSize),
			Z = Mathf.FloorToInt(position.z / cellSize)
		};
	}

	private void UpdateClusterProxyLight(ClusterProxyLightState cluster)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		if (cluster != null && !((Object)(object)cluster.ProxyLightObject == (Object)null) && !((Object)(object)cluster.ProxyLight == (Object)null) && cluster.MemberCount > 0)
		{
			float num = Mathf.Max(1, cluster.MemberCount);
			float num2 = Mathf.Min(1.75f, Mathf.Sqrt(num));
			cluster.ProxyLightObject.transform.position = cluster.PositionSum / num;
			cluster.ProxyLightObject.transform.rotation = cluster.Rotation;
			cluster.ProxyLightObject.SetActive(true);
			cluster.ProxyLight.type = (LightType)2;
			cluster.ProxyLight.color = cluster.ColorSum / num;
			cluster.ProxyLight.intensity = cluster.IntensitySum / num * Mathf.Max(0f, _staticLightIntensityMultiplier.Value) * num2;
			cluster.ProxyLight.range = cluster.RangeMax * Mathf.Max(0f, _staticLightRangeMultiplier.Value);
			cluster.ProxyLight.shadows = (LightShadows)0;
			((Behaviour)cluster.ProxyLight).enabled = true;
		}
	}

	private void EnsureProxyLight(Piece piece, FireOptimizationState state, Light sourceLight)
	{
		//IL_006d: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)piece == (Object)null) && !((Object)(object)sourceLight == (Object)null) && state != null)
		{
			if ((Object)(object)state.ProxyLightObject == (Object)null || (Object)(object)state.ProxyLight == (Object)null)
			{
				state.ProxyLightObject = AcquireProxyLight(out var proxyLight);
				state.ProxyLight = proxyLight;
			}
			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 MaintainFireOptimization(FireOptimizationState state, Light[] originalLights)
	{
		if (state != null)
		{
			Light firstOriginallyEnabledLight = GetFirstOriginallyEnabledLight(state, originalLights);
			if (state.AppliedMode == AppliedFireMode.FullCull || (Object)(object)firstOriginallyEnabledLight == (Object)null)
			{
				DisableProxyLight(state);
			}
			else if (_useClusteredFireProxyLights.Value)
			{
				DisableProxyLight(state);
				EnsureClusterProxyLight(state.Piece, firstOriginallyEnabledLight);
			}
			else
			{
				EnsureProxyLight(state.Piece, state, firstOriginallyEnabledLight);
			}
		}
	}

	private void DisableProxyLight(FireOptimizationState state)
	{
		if (state != null)
		{
			ReleaseProxyLightObject(state.ProxyLightObject);
			state.ProxyLightObject = null;
			state.ProxyLight = null;
		}
	}

	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 HitIsNonStructuralEffect(RaycastHit hit)
	{
		if ((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null)
		{
			return true;
		}
		if ((Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Piece>() != (Object)null)
		{
			return false;
		}
		if ((Object)(object)((RaycastHit)(ref hit)).collider.attachedRigidbody != (Object)null)
		{
			return true;
		}
		return (Object)(object)((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<ParticleSystem>() != (Object)null;
	}

	private bool HitIsKnownFirePiece(RaycastHit hit, Piece targetPiece)
	{
		if (_ignoreKnownFirePiecesInFireOcclusion == null || !_ignoreKnownFirePiecesInFireOcclusion.Value || (Object)(object)((RaycastHit)(ref hit)).collider == (Object)null)
		{
			return false;
		}
		Piece componentInParent = ((Component)((RaycastHit)(ref hit)).collider).GetComponentInParent<Piece>();
		if ((Object)(object)componentInParent == (Object)null || (Object)(object)componentInParent == (Object)(object)targetPiece)
		{
			return false;
		}
		return _fireCandidatePieces.Contains(componentInParent) || IsKnownFirePiece(componentInParent, GetCachedKnownFireNameTokens());
	}

	private bool FireOcclusionHitsSolidBlocker(RaycastHit[] hits, int hitCount, Piece piece)
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		if (hitCount <= 0)
		{
			return false;
		}
		Array.Sort(hits, 0, hitCount, FireOcclusionHitComparer);
		for (int i = 0; i < hitCount; i++)
		{
			RaycastHit hit = hits[i];
			if (!((Object)(object)((RaycastHit)(ref hit)).collider == (Object)null))
			{
				if (HitBelongsToPiece(hit, piece))
				{
					return false;
				}
				if (!HitIsNonStructuralEffect(hit) && !HitIsKnownFirePiece(hit, piece))
				{
					return true;
				}
			}
		}
		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_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: 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_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: 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);
		bool flag = FireOcclusionHitsSolidBlocker(hitCount: (!(num > 0f)) ? Physics.RaycastNonAlloc(position, val, _fireOcclusionHits, magnitude, -5, (QueryTriggerInteraction)1) : Physics.SphereCastNonAlloc(position, num, val, _fireOcclusionHits, magnitude, -5, (QueryTriggerInteraction)1), hits: _fireOcclusionHits, piece: piece);
		if (_debugFireOcclusion.Value)
		{
			Debug.DrawLine(position, fireTargetPosition, flag ? Color.red : Color.green, (_optimizerUpdateInterval != null) ? Mathf.Max(0.1f, _optimizerUpdateInterval.Value) : 0.5f);
		}
		return flag;
	}

	private void MarkFireOcclusionCheckBudget()
	{
		_fireOcclusionBudgetGeneration++;
		if (_fireOcclusionBudgetGeneration == 0)
		{
			_fireOcclusionBudgetGeneration = 1;
		}
		if (!_useFireOcclusionCulling.Value || _fireCandidates.Count == 0)
		{
			return;
		}
		int num = Mathf.Min(_fireCandidates.Count, Mathf.Max(0, _maxFireOcclusionChecksPerUpdate.Value));
		if (num == 0)
		{
			return;
		}
		if (_nextFireOcclusionStartIndex < 0 || _nextFireOcclusionStartIndex >= _fireCandidates.Count)
		{
			_nextFireOcclusionStartIndex = 0;
		}
		for (int i = 0; i < num; i++)
		{
			int index = (_nextFireOcclusionStartIndex + i) % _fireCandidates.Count;
			FireCandidate fireCandidate = _fireCandidates[index];
			if (fireCandidate != null)
			{
				fireCandidate.OcclusionCheckGeneration = _fireOcclusionBudgetGeneration;
			}
		}
		_nextFireOcclusionStartIndex = (_nextFireOcclusionStartIndex + num) % _fireCandidates.Count;
	}

	private bool GetBudgetedFireOcclusion(FireCandidate candidate, Piece piece, Light[] lights, MeshRenderer[] renderers, Camera mainCamera)
	{
		if (!_useFireOcclusionCulling.Value)
		{
			return false;
		}
		if (candidate == null)
		{
			return false;
		}
		float num = Mathf.Max(0.1f, _fireOcclusionCacheSeconds.Value);
		if (candidate.HasOcclusionResult && Time.time - candidate.LastOcclusionCheckTime < num)
		{
			return candidate.CachedOccluded;
		}
		if (candidate.OcclusionCheckGeneration != _fireOcclusionBudgetGeneration)
		{
			return candidate.HasOcclusionResult && candidate.CachedOccluded;
		}
		candidate.CachedOccluded = IsFireOccludedFromCamera(piece, lights, renderers, mainCamera);
		candidate.HasOcclusionResult = true;
		candidate.LastOcclusionCheckTime = Time.time;
		return candidate.CachedOccluded;
	}

	private void AddFireOptimizationStateMetrics(ref FireMetrics metrics)
	{
		//IL_01da: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e9: Invalid comparison between Unknown and I4
		foreach (FireOptimizationState value in _fireStates.Values)
		{
			if (value == null)
			{
				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.isPlaying)
				{
					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++;
				}
			}
		}
		foreach (ClusterProxyLightState value2 in _clusterProxyLights.Values)
		{
			if (value2 != null && (Object)(object)value2.ProxyLight != (Object)null && ((Behaviour)value2.ProxyLight).enabled && (Object)(object)value2.ProxyLightObject != (Object)null && value2.ProxyLightObject.activeInHierarchy)
			{
				metrics.FireProxyLightsActive++;
			}
		}
	}

	private void UpdateFireOptimizations()
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			if (HasActiveFireOptimizations())
			{
				RestoreAllFireOptimizations();
			}
			ResetFireCandidateCache();
			return;
		}
		Camera main = Camera.main;
		Plane[] frustumPlanes = null;
		if ((Object)(object)main != (Object)null)
		{
			GeometryUtility.CalculateFrustumPlanes(main, _fireFrustumPlanes);
			frustumPlanes = _fireFrustumPlanes;
		}
		float time = Time.time;
		if (!_hasRefreshedFireCandidates || _fireCandidateRefreshRequested || (_enablePeriodicFireCandidateRefresh.Value && time >= _nextFireCandidateRefreshTime))
		{
			RefreshFireCandidateCache();
		}
		FireMetrics metrics = default(FireMetrics);
		MarkFireOcclusionCheckBudget();
		BeginClusterProxyLightUpdate();
		for (int num = _fireCandidates.Count - 1; num >= 0; num--)
		{
			FireCandidate fireCandidate = _fireCandidates[num];
			if (fireCandidate == null || (Object)(object)fireCandidate.Piece == (Object)null)
			{
				if (fireCandidate != null)
				{
					_fireCandidatePieces.Remove(fireCandidate.Piece);
				}
				_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);
				_fireCandidatePieces.Remove(piece);
				_fireCandidates.RemoveAt(num);
				continue;
			}
			bool flag = IsPieceVisible(renderers, frustumPlanes);
			bool flag2 = flag && GetBudgetedFireOcclusion(fireCandidate, piece, originalLights, renderers, main);
			bool flag3 = _useFireVisibilityCulling.Value && !flag;
			bool flag4 = _useFireOcclusionCulling.Value && flag && flag2;
			bool flag5 = flag3 || flag4;
			bool flag6 = !flag5;
			metrics.FireCandidates++;
			if (flag)
			{
				metrics.RendererVisibleFireCandidates++;
			}
			if (flag2)
			{
				metrics.OccludedFireCandidates++;
			}
			if (flag6)
			{
				metrics.RelevantFireCandidates++;
			}
			else
			{
				metrics.HiddenOrIrrelevantFireCandidates++;
			}
			if (flag6)
			{
				fireCandidate.LastRelevantTime = time;
			}
			else
			{
				fireCandidate.LastIrrelevantTime = time;
			}
			bool flag7 = flag5 && time - fireCandidate.LastRelevantTime >= Mathf.Max(0f, _fireVisibilityGraceSeconds.Value);
			bool flag8 = flag6 && time - fireCandidate.LastIrrelevantTime >= Mathf.Max(0f, _fireRestoreGraceSeconds.Value);
			bool flag9 = _fireStates.ContainsKey(piece);
			AppliedFireMode appliedFireMode = ((_fireOptimizationMode.Value != FireOptimizationMode.StaticLight) ? AppliedFireMode.FullCull : AppliedFireMode.StaticLight);
			if (flag9 && (Object)(object)fireCandidate.Source != (Object)null && !fireCandidate.Source.IsBurning())
			{
				RestoreFire(piece);
				continue;
			}
			if (flag9)
			{
				if (flag8)
				{
					RestoreFire(piece);
					continue;
				}
				FireOptimizationState fireOptimizationState = _fireStates[piece];
				if (fireOptimizationState.AppliedMode == appliedFireMode)
				{
					MaintainFireOptimization(fireOptimizationState, originalLights);
					continue;
				}
			}
			else
			{
				bool flag10 = (((Object)(object)fireCandidate.Source != (Object)null) ? fireCandidate.Source.IsBurning() : HasActiveFireVisuals(originalLights, particles));
				if (!flag7 || !flag10)
				{
					continue;
				}
			}
			if (_fireOptimizationMode.Value == FireOptimizationMode.StaticLight)
			{
				ApplyStaticLight(piece, fireCandidate.Source, renderers, originalLights, particles);
			}
			else
			{
				ApplyFullCull(piece, fireCandidate.Source, renderers, originalLights, particles);
			}
		}
		FinalizeClusterProxyLightUpdate();
		EnforceOptimizedFireState();
		AddFireOptimizationStateMetrics(ref metrics);
		_fireMetrics = metrics;
		CleanupDestroyedFireStates();
	}

	private void Awake()
	{
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0455: Unknown result type (might be due to invalid IL or missing references)
		//IL_046a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0474: Expected O, but got Unknown
		_instance = this;
		_windowRect = _windowRectDefault;
		_enableProfiler = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "EnableProfiler", false, "Allows the F7 profiler window and manual snapshots. Leave this off during normal play for zero profiler UI or snapshot overhead.");
		_enableConsoleLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "EnableConsoleLogging", false, "Writes each completed profiler snapshot to the BepInEx log. Useful for comparisons, but repeated logging can create large files.");
		_showProfilerOnStart = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "ShowProfilerOnStart", false, "Opens the profiler window automatically after entering a world. The profiler must also be enabled.");
		_enableAutomaticProfilerPolling = ((BaseUnityPlugin)this).Config.Bind<bool>("Profiler", "EnableAutomaticPolling", false, "Refreshes profiler snapshots automatically while the window or logging is active. Leave this off in large worlds and use Poll now when needed.");
		_profilerPollInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Profiler", "PollIntervalSeconds", 5f, "Seconds between automatic snapshots. Short intervals feel more live but make expensive scene scans happen more often.");
		_toggleProfilerKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Profiler", "ToggleProfilerKey", (KeyCode)288, "Keyboard key that opens or closes the in-game profiler window.");
		_topLightOffenderCount = ((BaseUnityPlugin)this).Config.Bind<int>("Profiler", "TopLightOffenderCount", 8, "Maximum number of likely light-heavy build pieces shown after a snapshot. Set to zero to skip this ranking.");
		_enableFireOptimizations = ((BaseUnityPlugin)this).Config.Bind<bool>("Fire", "EnableFireOptimizations", false, "Optimizes known fire pieces when they are outside the camera view or safely hidden behind solid building geometry.");
		_fireOptimizationMode = ((BaseUnityPlugin)this).Config.Bind<FireOptimizationMode>("Fire", "Mode", FireOptimizationMode.StaticLight, "StaticLight keeps hidden areas lit with cheap shared lights. FullCull removes hidden fire lights and visual effects completely.");
		_optimizerUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Fire", "UpdateIntervalSeconds", 0.5f, "Seconds between fire visibility checks. Lower values react faster; higher values reduce optimizer CPU work.");
		_fireVisibilityGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Fire", "HiddenGraceSeconds", 1.5f, "Seconds a fire must stay hidden before its expensive light and effects are optimized. This prevents rapid camera movement from causing visual popping.");
		_fireRestoreGraceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Fire", "RestoreGraceSeconds", 1f, "Seconds a fire must stay visible or unblocked before its original light and effects return. This prevents rapid on/off cycling.");
		_enableWearNTearOptimizations = ((BaseUnityPlugin)this).Config.Bind<bool>("WearNTear", "EnableWearNTearOptimizations", false, "Stops structural-support and weather-wear calculations. Pieces still keep health, damage, repair, destruction, and saved network state.");
		_wearNTearBypassSupportValue = ((BaseUnityPlugin)this).Config.Bind<float>("WearNTear", "BypassedSupportValue", 1000000f, "Support strength reported while support calculations are bypassed. The large default makes every affected structure fully supported.");
		_knownFirePieceNameTokens = ((BaseUnityPlugin)this).Config.Bind<string>("Fire Discovery", "KnownFirePieceNameTokens", "piece_firepit,fire_pit,firepit,piece_hearth,hearth,piece_bonfire,bonfire,brazier,piece_brazier,standing_brazier,groundtorch,walltorch,piece_groundtorch,piece_walltorch,sconce", "Comma-separated internal name fragments used to recognize safe fire pieces. Only matching Fireplace pieces can enter the optimizer.");
		_fireCandidateRefreshInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Fire Discovery", "RefreshIntervalSeconds", 15f, "Seconds between optional fallback scans for Fireplace components. This setting matters only when the periodic safety refresh is enabled.");
		_enablePeriodicFireCandidateRefresh = ((BaseUnityPlugin)this).Config.Bind<bool>("Fire Discovery", "EnablePeriodicSafetyRefresh", false, "Runs an occasional full Fireplace scan as a discovery fallback. Normal discovery is event-driven, so leave this off unless a modded fire is missed.");
		_fireLightUpdateComponentNameTokens = ((BaseUnityPlugin)this).Config.Bind<string>("Fire Discovery", "LightUpdateComponentNameTokens", "lightflicker,lightlod", "Comma-separated script-name fragments for known fire light animation scripts. Matching scripts pause while their fire is hidden.");
		_useFireVisibilityCulling = ((BaseUnityPlugin)this).Config.Bind<bool>("Fire Visibility", "CullOutsideCamera", true, "Allows fires outside the camera's current field of view to use their hidden optimized state.");
		_useFireOcclusionCulling = ((BaseUnityPlugin)this).Config.Bind<bool>("Fire Visibility", "CullBehindGeometry", true, "Allows fires behind solid walls, floors, or roofs to use their hidden optimized state.");
		_ignoreKnow