Decompiled source of PerformantWorld v1.0.1

PerformantWorld.dll

Decompiled 19 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("PerformantWorld")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("PerformantWorld")]
[assembly: AssemblyTitle("PerformantWorld")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PerformantWorld
{
	internal static class DistantLightFlicker
	{
		private const string Section = "3 - Lights";

		private const float DefaultDistance = 25f;

		private const float MinDistance = 5f;

		private const float MaxDistance = 100f;

		private const float SettleSecondsAfterFadeIn = 1f;

		private static readonly FieldRef<LightFlicker, float> SecondsAlive = AccessTools.FieldRefAccess<LightFlicker, float>("m_time");

		internal static Feature Feature;

		private static ConfigEntry<float> flickerDistance;

		private static int lightsSkipped;

		private static int lightsChecked;

		private static int framesCounted;

		private static int lastCountedFrame = -1;

		private static float skippedPerFrameShown;

		private static float checkedPerFrameShown;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("3 - Lights", "Enabled", true, "Torches, fires and lamps further away than FlickerDistance stop flickering. Up close nothing changes. Short-lived lights, like lightning flashes, are never touched.");
			flickerDistance = config.Bind<float>("3 - Lights", "FlickerDistance", 25f, new ConfigDescription("Distance in metres from the camera within which lights keep flickering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			Feature = Features.Register("lights", "Light flicker", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(LightFlicker), "CustomUpdate", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DistantLightFlicker), "BeforeFlicker", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool BeforeFlicker(LightFlicker __instance)
		{
			//IL_0054: 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)
			if (!Feature.IsActive || !CameraPosition.TryGet(out var position))
			{
				return true;
			}
			bool collecting = MeasureOverlay.Collecting;
			if (collecting)
			{
				CountFrame();
				lightsChecked++;
			}
			if (IsTemporary(__instance) || IsStillFadingIn(__instance))
			{
				return true;
			}
			float value = flickerDistance.Value;
			Vector3 val = ((Component)__instance).transform.position - position;
			if (((Vector3)(ref val)).sqrMagnitude <= value * value)
			{
				return true;
			}
			if (collecting)
			{
				lightsSkipped++;
			}
			return false;
		}

		private static bool IsTemporary(LightFlicker light)
		{
			return light.m_ttl > 0f;
		}

		private static bool IsStillFadingIn(LightFlicker light)
		{
			return SecondsAlive.Invoke(light) < light.m_fadeInDuration + 1f;
		}

		private static void CountFrame()
		{
			if (lastCountedFrame != Time.frameCount)
			{
				lastCountedFrame = Time.frameCount;
				framesCounted++;
			}
		}

		internal static void RollStats()
		{
			int num = Mathf.Max(1, framesCounted);
			skippedPerFrameShown = (float)lightsSkipped / (float)num;
			checkedPerFrameShown = (float)lightsChecked / (float)num;
			lightsSkipped = 0;
			lightsChecked = 0;
			framesCounted = 0;
		}

		private static string Describe()
		{
			if (checkedPerFrameShown <= 0f)
			{
				return "";
			}
			return $"{skippedPerFrameShown:0} of {checkedPerFrameShown:0} lights skipped per frame";
		}
	}
	internal static class CameraPosition
	{
		private static int cachedFrame = -1;

		private static Vector3 cachedPosition;

		private static bool hasCamera;

		internal static bool TryGet(out Vector3 position)
		{
			//IL_0040: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (cachedFrame != Time.frameCount)
			{
				cachedFrame = Time.frameCount;
				Camera mainCamera = Utils.GetMainCamera();
				hasCamera = (Object)(object)mainCamera != (Object)null;
				if (hasCamera)
				{
					cachedPosition = ((Component)mainCamera).transform.position;
				}
			}
			position = cachedPosition;
			return hasCamera;
		}
	}
	internal static class FarAi
	{
		private sealed class CreatureState
		{
			internal float NextDistanceCheck;

			internal bool IsFar;

			internal float OwedSeconds;

			internal Character Character;
		}

		private const string Section = "5 - AI";

		private const float DefaultFullRateDistance = 50f;

		private const float MinFullRateDistance = 10f;

		private const float MaxFullRateDistance = 200f;

		private const float GameUpdateInterval = 0.05f;

		private const float DefaultFarInterval = 0.25f;

		private const float MaxFarInterval = 1f;

		private const float DistanceCheckSeconds = 1f;

		private const float DistanceCheckSpreadSeconds = 0.25f;

		private static readonly ConditionalWeakTable<BaseAI, CreatureState> States = new ConditionalWeakTable<BaseAI, CreatureState>();

		private static readonly HashSet<BaseAI> creaturesSeen = new HashSet<BaseAI>();

		private static readonly HashSet<BaseAI> farCreaturesSeen = new HashSet<BaseAI>();

		internal static Feature Feature;

		private static ConfigEntry<float> fullRateDistance;

		private static ConfigEntry<float> farInterval;

		private static int creaturesShown;

		private static int farCreaturesShown;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("5 - AI", "Enabled", true, "Creatures with no player nearby think less often. The game runs creature AI 20 times a second; far away creatures run it at FarInterval instead, with the same total time, so their timers stay right. Bosses always run at full rate.");
			fullRateDistance = config.Bind<float>("5 - AI", "FullRateDistance", 50f, new ConfigDescription("Creatures within this many metres of a player always think at full rate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 200f), Array.Empty<object>()));
			farInterval = config.Bind<float>("5 - AI", "FarInterval", 0.25f, new ConfigDescription("Seconds between AI updates for far away creatures. The game's own rate is " + 0.05f.ToString(CultureInfo.InvariantCulture) + ".", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			Feature = Features.Register("ai", "Far AI", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(MonsterAI), "UpdateAI", new Type[1] { typeof(float) }, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(AnimalAI), "UpdateAI", new Type[1] { typeof(float) }, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				return false;
			}
			if (methodInfo.DeclaringType != typeof(MonsterAI) || methodInfo2.DeclaringType != typeof(AnimalAI))
			{
				return false;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(FarAi), "BeforeAiUpdate", (Type[])null)
			{
				priority = 800
			};
			harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool BeforeAiUpdate(BaseAI __instance, ref float dt, ref bool __result)
		{
			CreatureState orCreateValue = States.GetOrCreateValue(__instance);
			if (!Feature.IsActive)
			{
				HandBackOwedTime(orCreateValue, ref dt);
				return true;
			}
			RefreshDistance(__instance, orCreateValue);
			if (MeasureOverlay.Collecting)
			{
				creaturesSeen.Add(__instance);
			}
			if (!orCreateValue.IsFar)
			{
				HandBackOwedTime(orCreateValue, ref dt);
				return true;
			}
			if (MeasureOverlay.Collecting)
			{
				farCreaturesSeen.Add(__instance);
			}
			orCreateValue.OwedSeconds += dt;
			if (orCreateValue.OwedSeconds < farInterval.Value)
			{
				__result = true;
				return false;
			}
			dt = orCreateValue.OwedSeconds;
			orCreateValue.OwedSeconds = 0f;
			return true;
		}

		private static void HandBackOwedTime(CreatureState state, ref float dt)
		{
			if (!(state.OwedSeconds <= 0f))
			{
				dt += state.OwedSeconds;
				state.OwedSeconds = 0f;
			}
		}

		private static void RefreshDistance(BaseAI ai, CreatureState state)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			if (!(time < state.NextDistanceCheck))
			{
				state.NextDistanceCheck = time + 1f + Random.value * 0.25f;
				if ((Object)(object)state.Character == (Object)null)
				{
					state.Character = ((Component)ai).GetComponent<Character>();
				}
				bool flag = (Object)(object)state.Character != (Object)null && state.Character.IsBoss();
				state.IsFar = !flag && !PlayerNearby(((Component)ai).transform.position, fullRateDistance.Value);
			}
		}

		private static bool PlayerNearby(Vector3 position, float range)
		{
			//IL_0000: 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_0042: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			if (Player.IsPlayerInRange(position, range))
			{
				return true;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsDedicated())
			{
				return false;
			}
			float num = range * range;
			foreach (ZNetPeer peer in instance.GetPeers())
			{
				Vector3 val = peer.GetRefPos() - position;
				if (((Vector3)(ref val)).sqrMagnitude < num)
				{
					return true;
				}
			}
			return false;
		}

		internal static void RollStats()
		{
			creaturesShown = creaturesSeen.Count;
			farCreaturesShown = farCreaturesSeen.Count;
			creaturesSeen.Clear();
			farCreaturesSeen.Clear();
		}

		private static string Describe()
		{
			if (creaturesShown == 0)
			{
				return "";
			}
			return farCreaturesShown + " of " + creaturesShown + " creatures at the reduced rate";
		}
	}
	internal sealed class Feature
	{
		internal const string UnavailableAfterGameChange = "unavailable";

		internal const string UnavailableAfterError = "turned off after an error (see log)";

		internal readonly string Key;

		internal readonly string Label;

		internal readonly ConfigEntry<bool> Enabled;

		private readonly Func<string> describe;

		internal string UnavailableReason { get; private set; }

		internal bool IsAvailable => UnavailableReason == null;

		internal bool IsActive
		{
			get
			{
				if (IsAvailable && Enabled.Value)
				{
					return !Features.AllOff;
				}
				return false;
			}
		}

		internal string Status
		{
			get
			{
				if (IsAvailable)
				{
					if (!IsActive)
					{
						return "off";
					}
					return "on";
				}
				return "off - " + UnavailableReason;
			}
		}

		internal string OverlayLine
		{
			get
			{
				if (!IsAvailable)
				{
					return "off - " + Label + " - " + UnavailableReason;
				}
				string text = Describe();
				string text2 = (IsActive ? "on" : "off") + "  " + Label;
				if (text.Length <= 0)
				{
					return text2;
				}
				return text2 + " - " + text;
			}
		}

		internal Feature(string key, string label, ConfigEntry<bool> enabled, Func<string> describe)
		{
			Key = key;
			Label = label;
			Enabled = enabled;
			this.describe = describe;
		}

		internal string Describe()
		{
			if (!IsActive)
			{
				return "";
			}
			return describe();
		}

		internal void MakeUnavailable(string reason)
		{
			UnavailableReason = reason;
		}
	}
	internal static class Features
	{
		internal static readonly List<Feature> All = new List<Feature>();

		internal static bool AllOff;

		internal static Feature Register(string key, string label, ConfigEntry<bool> enabled, Func<string> describe)
		{
			Feature feature = new Feature(key, label, enabled, describe);
			All.Add(feature);
			return feature;
		}

		internal static Feature Find(string key)
		{
			foreach (Feature item in All)
			{
				if (item.Key == key)
				{
					return item;
				}
			}
			return null;
		}

		internal static void RunSafely(Feature feature, Action step)
		{
			if (!feature.IsAvailable)
			{
				return;
			}
			try
			{
				step();
			}
			catch (Exception ex)
			{
				PerformantWorldPlugin.Log.LogWarning((object)(feature.Label + " failed and is turned off: " + ex));
				feature.MakeUnavailable("turned off after an error (see log)");
			}
		}
	}
	internal static class FogCulling
	{
		private const string Section = "11 - Fog Culling";

		private const float DefaultMargin = 1.25f;

		private const float MinMargin = 1f;

		private const float MaxMargin = 3f;

		private const float DefaultMinimumDistance = 40f;

		private const float MinMinimumDistance = 10f;

		private const float MaxMinimumDistance = 200f;

		private const float FullyFoggedTransmittance = 0.01f;

		private const float RefreshSeconds = 0.1f;

		private const float UseFarClipPlane = 0f;

		private const int LayerCount = 32;

		private const float NotCaptured = -1f;

		private static readonly string[] CulledLayerNames = new string[11]
		{
			"Default", "Default_small", "static_solid", "piece", "piece_nonsolid", "item", "vehicle", "character", "character_net", "character_noenv",
			"effect"
		};

		private static readonly float[] cullDistances = new float[32];

		private static readonly List<int> culledLayers = new List<int>();

		internal static Feature Feature;

		private static ConfigEntry<float> margin;

		private static ConfigEntry<float> minimumDistance;

		private static ConfigEntry<bool> cullShadows;

		private static Camera culledCamera;

		private static float[] cameraOriginalDistances;

		private static bool cameraOriginalSpherical;

		private static bool cameraCulled;

		private static float gameShadowDistance = -1f;

		private static float appliedShadowDistance = -1f;

		private static float nextRefresh;

		private static bool insideDungeon;

		private static float visibleDistance = float.PositiveInfinity;

		private static float cullDistance = float.PositiveInfinity;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("11 - Fog Culling", "Enabled", true, "Objects and shadows the fog completely hides aren't drawn, so the graphics card does less in the swamp, in storms, at night and in Mistlands fog. Sky, terrain and water are always drawn, everything comes back as the fog clears, and nothing is culled inside dungeons.");
			margin = config.Bind<float>("11 - Fog Culling", "Margin", 1.25f, new ConfigDescription("How far past the point where fog becomes solid things are still drawn, as a multiple of that distance. Raise it if big objects vanish before the fog covers them.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
			minimumDistance = config.Bind<float>("11 - Fog Culling", "MinimumDistance", 40f, new ConfigDescription("Nothing closer than this many metres is ever hidden, however thick the fog.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 200f), Array.Empty<object>()));
			cullShadows = config.Bind<bool>("11 - Fog Culling", "CullShadows", true, "Also stop drawing sun and moon shadows beyond the same distance.");
			Feature = Features.Register("fog", "Fog culling", enabled, Describe);
			string[] culledLayerNames = CulledLayerNames;
			for (int i = 0; i < culledLayerNames.Length; i++)
			{
				int num = LayerMask.NameToLayer(culledLayerNames[i]);
				if (num >= 0)
				{
					culledLayers.Add(num);
				}
			}
		}

		internal static void Sync()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)mainCamera != (Object)(object)culledCamera)
			{
				SwitchCamera(mainCamera);
			}
			insideDungeon = (Object)(object)mainCamera != (Object)null && Character.InInterior(((Component)mainCamera).transform.position);
			if (!Feature.IsActive || (Object)(object)mainCamera == (Object)null || insideDungeon)
			{
				RestoreCamera();
				RestoreShadows();
				visibleDistance = float.PositiveInfinity;
			}
			else if (!(Time.unscaledTime < nextRefresh))
			{
				nextRefresh = Time.unscaledTime + 0.1f;
				visibleDistance = FogVisibleDistance();
				cullDistance = Mathf.Max(minimumDistance.Value, visibleDistance * margin.Value);
				bool num = cullDistance < mainCamera.farClipPlane;
				if (num)
				{
					CullBeyond(mainCamera, cullDistance);
				}
				else
				{
					RestoreCamera();
				}
				if (num && cullShadows.Value)
				{
					LimitShadows(cullDistance);
				}
				else
				{
					RestoreShadows();
				}
			}
		}

		private static float FogVisibleDistance()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected I4, but got Unknown
			if (!RenderSettings.fog)
			{
				return float.PositiveInfinity;
			}
			float fogDensity = RenderSettings.fogDensity;
			float num = 0f - Mathf.Log(0.01f);
			FogMode fogMode = RenderSettings.fogMode;
			switch (fogMode - 1)
			{
			case 0:
				return RenderSettings.fogEndDistance;
			case 1:
				if (!(fogDensity > 0f))
				{
					return float.PositiveInfinity;
				}
				return num / fogDensity;
			case 2:
				if (!(fogDensity > 0f))
				{
					return float.PositiveInfinity;
				}
				return Mathf.Sqrt(num) / fogDensity;
			default:
				return float.PositiveInfinity;
			}
		}

		private static void SwitchCamera(Camera camera)
		{
			RestoreCamera();
			culledCamera = camera;
			if (!((Object)(object)camera == (Object)null))
			{
				cameraOriginalDistances = camera.layerCullDistances;
				cameraOriginalSpherical = camera.layerCullSpherical;
			}
		}

		private static void CullBeyond(Camera camera, float distance)
		{
			Array.Copy(cameraOriginalDistances, cullDistances, 32);
			foreach (int culledLayer in culledLayers)
			{
				float num = cullDistances[culledLayer];
				cullDistances[culledLayer] = ((num == 0f) ? distance : Mathf.Min(num, distance));
			}
			camera.layerCullSpherical = true;
			camera.layerCullDistances = cullDistances;
			cameraCulled = true;
		}

		private static void RestoreCamera()
		{
			if (cameraCulled)
			{
				cameraCulled = false;
				if (!((Object)(object)culledCamera == (Object)null) && cameraOriginalDistances != null)
				{
					culledCamera.layerCullDistances = cameraOriginalDistances;
					culledCamera.layerCullSpherical = cameraOriginalSpherical;
				}
			}
		}

		private static void LimitShadows(float distance)
		{
			bool flag = !Mathf.Approximately(QualitySettings.shadowDistance, appliedShadowDistance);
			if (gameShadowDistance == -1f || flag)
			{
				gameShadowDistance = QualitySettings.shadowDistance;
			}
			appliedShadowDistance = Mathf.Min(gameShadowDistance, distance);
			QualitySettings.shadowDistance = appliedShadowDistance;
		}

		private static void RestoreShadows()
		{
			if (gameShadowDistance != -1f)
			{
				if (Mathf.Approximately(QualitySettings.shadowDistance, appliedShadowDistance))
				{
					QualitySettings.shadowDistance = gameShadowDistance;
				}
				gameShadowDistance = -1f;
				appliedShadowDistance = -1f;
			}
		}

		private static string Describe()
		{
			if (insideDungeon)
			{
				return "inside a dungeon, nothing culled";
			}
			if (float.IsInfinity(visibleDistance))
			{
				return "no fog";
			}
			if ((Object)(object)culledCamera != (Object)null && cullDistance >= culledCamera.farClipPlane)
			{
				return $"fog solid at {visibleDistance:0} m, nothing hidden";
			}
			return $"fog solid at {visibleDistance:0} m, hiding past {cullDistance:0} m";
		}
	}
	internal sealed class FrameHistory
	{
		private const int Capacity = 2400;

		private const float MillisecondsPerSecond = 1000f;

		private const int FramesPerWorstFrame = 100;

		private readonly float[] frameSeconds = new float[2400];

		private readonly List<float> windowFrames = new List<float>(2400);

		private int nextSlot;

		private int recordedFrames;

		private int framesInWindow;

		internal float FramesPerSecond { get; private set; }

		internal float AverageMilliseconds { get; private set; }

		internal float WorstMilliseconds { get; private set; }

		internal void Clear()
		{
			nextSlot = 0;
			recordedFrames = 0;
			framesInWindow = 0;
		}

		internal void Record(float seconds)
		{
			frameSeconds[nextSlot] = seconds;
			nextSlot = (nextSlot + 1) % 2400;
			if (recordedFrames < 2400)
			{
				recordedFrames++;
			}
		}

		internal void Summarize(float windowSeconds)
		{
			windowFrames.Clear();
			float num = 0f;
			for (int i = 0; i < recordedFrames; i++)
			{
				if (!(num < windowSeconds))
				{
					break;
				}
				float num2 = frameSeconds[SlotFramesAgo(i)];
				windowFrames.Add(num2);
				num += num2;
			}
			framesInWindow = windowFrames.Count;
			if (framesInWindow != 0 && !(num <= 0f))
			{
				windowFrames.Sort();
				FramesPerSecond = (float)framesInWindow / num;
				AverageMilliseconds = num / (float)framesInWindow * 1000f;
				WorstMilliseconds = AverageOfSlowest(Mathf.Max(1, framesInWindow / 100)) * 1000f;
			}
		}

		private float AverageOfSlowest(int count)
		{
			float num = 0f;
			for (int i = windowFrames.Count - count; i < windowFrames.Count; i++)
			{
				num += windowFrames[i];
			}
			return num / (float)count;
		}

		private int SlotFramesAgo(int framesAgo)
		{
			return (nextSlot - 1 - framesAgo + 2400) % 2400;
		}
	}
	internal static class GameUiScale
	{
		private const float FallbackReferenceWidth = 1920f;

		private const float FallbackReferenceHeight = 1080f;

		private const float FallbackSliderScale = 1f;

		private const float SmallestUsableScale = 0.01f;

		private const float UnscaledSize = 1f;

		private static readonly FieldInfo SliderScale = AccessTools.Field(typeof(GuiScaler), "m_largeGuiScale");

		private static readonly FieldInfo ReferenceWidth = AccessTools.Field(typeof(GuiScaler), "m_minWidth");

		private static readonly FieldInfo ReferenceHeight = AccessTools.Field(typeof(GuiScaler), "m_minHeight");

		internal static float Current()
		{
			float num = ReadPositive(ReferenceWidth, 1920f);
			float num2 = ReadPositive(ReferenceHeight, 1080f);
			float num3 = ReadPositive(SliderScale, 1f);
			float num4 = Mathf.Min((float)Screen.width / num, (float)Screen.height / num2) * num3;
			if (!(num4 > 0.01f))
			{
				return 1f;
			}
			return num4;
		}

		private static float ReadPositive(FieldInfo field, float fallback)
		{
			if (field == null || !field.IsStatic)
			{
				return fallback;
			}
			object value = field.GetValue(null);
			if (value is float num && num > 0f)
			{
				return num;
			}
			if (value is int num2 && num2 > 0)
			{
				return num2;
			}
			return fallback;
		}
	}
	internal static class GpuInstancing
	{
		private const string Section = "7 - Rendering";

		private static readonly HashSet<Material> changedMaterials = new HashSet<Material>();

		internal static Feature Feature;

		private static bool applied;

		internal static void BindSettings(ConfigFile config)
		{
			ConfigEntry<bool> enabled = config.Bind<bool>("7 - Rendering", "GpuInstancing", false, "Lets the graphics card draw identical objects, like rows of the same wall piece or rocks, together in one go. Results depend on your scene and graphics card, so measure it. If anything looks wrong while it's on, turn it off; it switches back right away.");
			Feature = Features.Register("instancing", "GPU instancing", enabled, Describe);
		}

		internal static void Sync()
		{
			bool isActive = Feature.IsActive;
			if (isActive == applied)
			{
				return;
			}
			if (isActive)
			{
				if ((Object)(object)ZNetScene.instance == (Object)null)
				{
					return;
				}
				TurnOnForWorldMaterials();
			}
			else
			{
				TurnOffAgain();
			}
			applied = isActive;
		}

		private static void TurnOnForWorldMaterials()
		{
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				MeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<MeshRenderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					Material[] sharedMaterials = ((Renderer)componentsInChildren[i]).sharedMaterials;
					for (int j = 0; j < sharedMaterials.Length; j++)
					{
						TurnOn(sharedMaterials[j]);
					}
				}
			}
		}

		private static void TurnOn(Material material)
		{
			if (!((Object)(object)material == (Object)null) && !material.enableInstancing)
			{
				material.enableInstancing = true;
				changedMaterials.Add(material);
			}
		}

		private static void TurnOffAgain()
		{
			foreach (Material changedMaterial in changedMaterials)
			{
				if ((Object)(object)changedMaterial != (Object)null)
				{
					changedMaterial.enableInstancing = false;
				}
			}
			changedMaterials.Clear();
		}

		private static string Describe()
		{
			if (!applied)
			{
				return "";
			}
			return changedMaterials.Count + " materials";
		}
	}
	internal static class GrassCache
	{
		private sealed class InstancedPart
		{
			internal GameObject Prefab;

			internal Matrix4x4[] Matrices;
		}

		private sealed class LoosePart
		{
			internal GameObject Prefab;

			internal Vector3 Position;

			internal Quaternion Rotation;
		}

		private sealed class CachedPatch
		{
			internal Vector2Int Id;

			internal Vector3 Center;

			internal Quality Quality;

			internal float AmountScale;

			internal readonly List<InstancedPart> Instanced = new List<InstancedPart>();

			internal readonly List<LoosePart> Loose = new List<LoosePart>();

			internal LinkedListNode<Vector2Int> Node;
		}

		private const string Section = "8 - Grass";

		private const int DefaultMaxPatches = 1500;

		private const int MinMaxPatches = 100;

		private const int MaxMaxPatches = 20000;

		private const float UseGameDistance = 0f;

		private const float MaxGrassDistance = 150f;

		private const float ShortestGrassDistance = 10f;

		private const float UnknownDistance = -1f;

		private const float StatsWindowSeconds = 10f;

		private const string CloneSuffix = "(Clone)";

		private static readonly Type PatchDataType = AccessTools.Inner(typeof(ClutterSystem), "PatchData");

		private static readonly FieldInfo PatchCenter = AccessTools.Field(PatchDataType, "center");

		private static readonly FieldInfo PatchObjects = AccessTools.Field(PatchDataType, "m_objects");

		private static readonly MethodInfo AllocatePatch = AccessTools.Method(typeof(ClutterSystem), "AllocatePatch", (Type[])null, (Type[])null);

		private static readonly FieldRef<ClutterSystem, Quality> QualityOf = AccessTools.FieldRefAccess<ClutterSystem, Quality>("m_quality");

		private static readonly FieldRef<ClutterSystem, GameObject> GrassRootOf = AccessTools.FieldRefAccess<ClutterSystem, GameObject>("m_grassRoot");

		private static readonly FieldRef<InstanceRenderer, Matrix4x4[]> InstancesOf = AccessTools.FieldRefAccess<InstanceRenderer, Matrix4x4[]>("m_instances");

		private static readonly FieldRef<InstanceRenderer, int> InstanceCountOf = AccessTools.FieldRefAccess<InstanceRenderer, int>("m_instanceCount");

		private static readonly Type[] PatchClasses = new Type[6]
		{
			typeof(ClutterAwakePatch),
			typeof(ClutterDestroyPatch),
			typeof(ClutterSettingsPatch),
			typeof(ClutterResetPatch),
			typeof(ClutterGeneratePatchPatch),
			typeof(ClutterGenerateVegPatchPatch)
		};

		private static readonly Dictionary<Vector2Int, CachedPatch> cache = new Dictionary<Vector2Int, CachedPatch>();

		private static readonly LinkedList<Vector2Int> oldestFirst = new LinkedList<Vector2Int>();

		private static readonly Dictionary<string, GameObject> prefabsByName = new Dictionary<string, GameObject>();

		private static ClutterSystem prefabsBelongTo;

		private static float gameDistance = -1f;

		internal static Feature Feature;

		private static ConfigEntry<int> maxPatches;

		private static ConfigEntry<float> grassDistance;

		internal static bool WasReplayed;

		private static int fromMemory;

		private static int builtFresh;

		private static int fromMemoryShown;

		private static int builtFreshShown;

		private static float nextStatsWindow;

		private static bool InternalsFound
		{
			get
			{
				if (PatchDataType != null && PatchCenter != null && PatchObjects != null)
				{
					return AllocatePatch != null;
				}
				return false;
			}
		}

		private static bool Enabled
		{
			get
			{
				if (Feature.IsActive)
				{
					return InternalsFound;
				}
				return false;
			}
		}

		internal static void BindSettings(ConfigFile config)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("8 - Grass", "Enabled", true, "Remember where grass was placed so walking back into an area doesn't have to work it all out again. It looks exactly the same as without it. (Formerly PerformantGrass.)");
			maxPatches = config.Bind<int>("8 - Grass", "MaxCachedPatches", 1500, new ConfigDescription("How many 8x8 m grass patches to remember. Each one is a few kilobytes; the oldest are forgotten first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(100, 20000), Array.Empty<object>()));
			grassDistance = config.Bind<float>("8 - Grass", "GrassDistance", 0f, new ConfigDescription("How far away grass is drawn, in meters. 0 = the game's own distance (40). Higher values show more grass but cost more: the number of grass patches grows with the square of the distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 150f), Array.Empty<object>()));
			grassDistance.SettingChanged += delegate
			{
				if (Feature.IsAvailable)
				{
					ApplyDistance(ClutterSystem.instance, rebuild: true);
				}
			};
			Feature = Features.Register("grass", "Grass cache", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			if (!InternalsFound)
			{
				return false;
			}
			Type[] patchClasses = PatchClasses;
			foreach (Type type in patchClasses)
			{
				harmony.CreateClassProcessor(type).Patch();
			}
			return true;
		}

		internal static void ApplyDistance(ClutterSystem system, bool rebuild)
		{
			if ((Object)(object)system == (Object)null || system.m_menuHack)
			{
				return;
			}
			if (gameDistance == -1f)
			{
				gameDistance = system.m_distance;
			}
			float num = ((grassDistance.Value > 0f) ? Mathf.Max(10f, grassDistance.Value) : gameDistance);
			if (!Mathf.Approximately(system.m_distance, num))
			{
				system.m_distance = num;
				if (rebuild)
				{
					system.ClearAll();
				}
			}
		}

		internal static void AdjustLod(ClutterSystem system, object patchData)
		{
			if (patchData == null || !(PatchObjects.GetValue(patchData) is List<GameObject> list))
			{
				return;
			}
			float num = ((gameDistance > 0f) ? (system.m_distance / gameDistance) : 1f);
			float num2 = system.m_distance - system.m_grassPatchSize / 2f;
			foreach (GameObject item in list)
			{
				InstanceRenderer val = (((Object)(object)item != (Object)null) ? item.GetComponent<InstanceRenderer>() : null);
				if (!((Object)(object)val == (Object)null) && val.m_useLod)
				{
					GameObject val2 = PrefabFor(system, item);
					InstanceRenderer val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<InstanceRenderer>() : null);
					if (!((Object)(object)val3 == (Object)null))
					{
						val.m_lodMinDistance = val3.m_lodMinDistance * num;
						val.m_lodMaxDistance = Mathf.Min(val3.m_lodMaxDistance * num, num2);
					}
				}
			}
		}

		internal static void Clear()
		{
			cache.Clear();
			oldestFirst.Clear();
			prefabsByName.Clear();
			prefabsBelongTo = null;
		}

		internal static bool Has(ClutterSystem system, Vector2Int id)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			CachedPatch patch;
			if (Enabled)
			{
				return TryGet(system, id, out patch);
			}
			return false;
		}

		private static bool TryGet(ClutterSystem system, Vector2Int id, out CachedPatch patch)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!cache.TryGetValue(id, out patch))
			{
				return false;
			}
			if ((int)patch.Quality == (int)QualityOf.Invoke(system) && Mathf.Approximately(patch.AmountScale, system.m_amountScale))
			{
				return true;
			}
			Forget(id);
			patch = null;
			return false;
		}

		private static void Forget(Vector2Int id)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (cache.TryGetValue(id, out var value))
			{
				oldestFirst.Remove(value.Node);
				cache.Remove(id);
			}
		}

		internal static void ForgetArea(ClutterSystem system, Vector3 center, float radius)
		{
			//IL_002e: 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)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0067: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			float num = system.m_grassPatchSize / 2f;
			List<Vector2Int> list = new List<Vector2Int>();
			foreach (CachedPatch value in cache.Values)
			{
				Vector3 center2 = value.Center;
				bool num2 = center2.x + num < center.x - radius || center2.x - num > center.x + radius;
				bool flag = center2.z + num < center.z - radius || center2.z - num > center.z + radius;
				if (!num2 && !flag)
				{
					list.Add(value.Id);
				}
			}
			foreach (Vector2Int item in list)
			{
				Forget(item);
			}
		}

		internal static object Replay(ClutterSystem system, Vector2Int id)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled || !TryGet(system, id, out var patch))
			{
				return null;
			}
			GameObject val = GrassRootOf.Invoke(system);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			object obj = AllocatePatch.Invoke(system, null);
			PatchCenter.SetValue(obj, patch.Center);
			List<GameObject> list = (List<GameObject>)PatchObjects.GetValue(obj);
			float num = system.m_distance - system.m_grassPatchSize / 2f;
			foreach (InstancedPart item in patch.Instanced)
			{
				if ((Object)(object)item.Prefab == (Object)null)
				{
					continue;
				}
				GameObject val2 = Object.Instantiate<GameObject>(item.Prefab, patch.Center, Quaternion.identity, val.transform);
				InstanceRenderer component = val2.GetComponent<InstanceRenderer>();
				if ((Object)(object)component != (Object)null)
				{
					if (component.m_lodMaxDistance > num)
					{
						component.m_lodMaxDistance = num;
					}
					Matrix4x4[] matrices = item.Matrices;
					foreach (Matrix4x4 val3 in matrices)
					{
						component.AddInstance(val3);
					}
				}
				list.Add(val2);
			}
			foreach (LoosePart item2 in patch.Loose)
			{
				if (!((Object)(object)item2.Prefab == (Object)null))
				{
					list.Add(Object.Instantiate<GameObject>(item2.Prefab, item2.Position, item2.Rotation, val.transform));
				}
			}
			oldestFirst.Remove(patch.Node);
			oldestFirst.AddLast(patch.Node);
			if (MeasureOverlay.Collecting)
			{
				fromMemory++;
			}
			return obj;
		}

		internal static void Remember(ClutterSystem system, Vector2Int id, object patchData)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: 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_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			if (MeasureOverlay.Collecting)
			{
				builtFresh++;
			}
			if (!Enabled || patchData == null || system.m_menuHack || !(PatchObjects.GetValue(patchData) is List<GameObject> list))
			{
				return;
			}
			CachedPatch cachedPatch = new CachedPatch
			{
				Id = id,
				Center = (Vector3)PatchCenter.GetValue(patchData),
				Quality = QualityOf.Invoke(system),
				AmountScale = system.m_amountScale
			};
			foreach (GameObject item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					GameObject val = PrefabFor(system, item);
					if ((Object)(object)val == (Object)null)
					{
						return;
					}
					InstanceRenderer component = item.GetComponent<InstanceRenderer>();
					if ((Object)(object)component != (Object)null)
					{
						cachedPatch.Instanced.Add(CopyInstances(val, component));
						continue;
					}
					cachedPatch.Loose.Add(new LoosePart
					{
						Prefab = val,
						Position = item.transform.position,
						Rotation = item.transform.rotation
					});
				}
			}
			Forget(id);
			cachedPatch.Node = oldestFirst.AddLast(id);
			cache[id] = cachedPatch;
			while (cache.Count > maxPatches.Value && oldestFirst.First != null)
			{
				Forget(oldestFirst.First.Value);
			}
		}

		private static InstancedPart CopyInstances(GameObject prefab, InstanceRenderer renderer)
		{
			int num = InstanceCountOf.Invoke(renderer);
			Matrix4x4[] array = (Matrix4x4[])(object)new Matrix4x4[num];
			Array.Copy(InstancesOf.Invoke(renderer), array, num);
			return new InstancedPart
			{
				Prefab = prefab,
				Matrices = array
			};
		}

		private static GameObject PrefabFor(ClutterSystem system, GameObject placed)
		{
			if ((Object)(object)prefabsBelongTo != (Object)(object)system)
			{
				prefabsByName.Clear();
				prefabsBelongTo = system;
				foreach (Clutter item in system.m_clutter)
				{
					if ((Object)(object)item.m_prefab != (Object)null)
					{
						prefabsByName[((Object)item.m_prefab).name] = item.m_prefab;
					}
				}
			}
			string text = ((Object)placed).name;
			int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				text = text.Substring(0, num);
			}
			if (!prefabsByName.TryGetValue(text.Trim(), out var value))
			{
				return null;
			}
			return value;
		}

		internal static void RollStats()
		{
			if (!(Time.unscaledTime < nextStatsWindow))
			{
				nextStatsWindow = Time.unscaledTime + 10f;
				fromMemoryShown = fromMemory;
				builtFreshShown = builtFresh;
				fromMemory = 0;
				builtFresh = 0;
			}
		}

		private static string Describe()
		{
			return $"last {10f:0}s: {fromMemoryShown} patches from memory, {builtFreshShown} built; {cache.Count} remembered";
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "Awake")]
	internal static class ClutterAwakePatch
	{
		private static void Postfix(ClutterSystem __instance)
		{
			GrassCache.Clear();
			if (!__instance.m_menuHack)
			{
				GrassCache.ApplyDistance(__instance, rebuild: false);
			}
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "OnDestroy")]
	internal static class ClutterDestroyPatch
	{
		private static void Postfix()
		{
			GrassCache.Clear();
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "ApplySettings")]
	internal static class ClutterSettingsPatch
	{
		private static void Postfix()
		{
			GrassCache.Clear();
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "ResetGrass")]
	internal static class ClutterResetPatch
	{
		private static void Prefix(ClutterSystem __instance, Vector3 center, float radius)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			GrassCache.ForgetArea(__instance, center, radius);
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "GeneratePatch")]
	internal static class ClutterGeneratePatchPatch
	{
		private static void Prefix(ClutterSystem __instance, Vector2Int p, ref bool generated, out bool __state)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			__state = generated;
			if (GrassCache.Has(__instance, p))
			{
				generated = false;
			}
		}

		private static void Postfix(ref bool generated, bool __state)
		{
			if (GrassCache.WasReplayed)
			{
				generated = __state;
			}
			GrassCache.WasReplayed = false;
		}
	}
	[HarmonyPatch(typeof(ClutterSystem), "GenerateVegPatch")]
	internal static class ClutterGenerateVegPatchPatch
	{
		private static bool Prefix(ClutterSystem __instance, Vector2Int patchID, ref object __result, out bool __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			object obj = GrassCache.Replay(__instance, patchID);
			__state = obj != null;
			GrassCache.WasReplayed = __state;
			if (!__state)
			{
				return true;
			}
			__result = obj;
			return false;
		}

		private static void Postfix(ClutterSystem __instance, Vector2Int patchID, object __result, bool __state)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if (!__state)
			{
				GrassCache.Remember(__instance, patchID, __result);
			}
			if (!__instance.m_menuHack)
			{
				GrassCache.AdjustLod(__instance, __result);
			}
		}
	}
	internal sealed class MeasureOverlay : MonoBehaviour
	{
		private const string Section = "1 - Measure";

		private const float DefaultMeasureSeconds = 3f;

		private const float MinMeasureSeconds = 1f;

		private const float MaxMeasureSeconds = 10f;

		private const float DefaultOverlayScale = 1f;

		private const float MinOverlayScale = 0.5f;

		private const float MaxOverlayScale = 2f;

		private const float RefreshSeconds = 0.5f;

		private const int FontSize = 14;

		private const int HorizontalPadding = 8;

		private const int VerticalPadding = 6;

		private const float ScreenMargin = 10f;

		private const KeyCode DefaultOverlayKey = (KeyCode)288;

		private const KeyCode DefaultCompareKey = (KeyCode)289;

		private const KeyCode PreviousOverlayKey = (KeyCode)289;

		private const KeyCode PreviousCompareKey = (KeyCode)290;

		internal static ConfigEntry<bool> ShowOverlay;

		private static bool startFreshRequested;

		private static ConfigEntry<KeyboardShortcut> overlayKey;

		private static ConfigEntry<KeyboardShortcut> compareKey;

		private static ConfigEntry<float> measureSeconds;

		private static ConfigEntry<float> overlayScale;

		private readonly FrameHistory history = new FrameHistory();

		private readonly StringBuilder text = new StringBuilder();

		private string shownText = "";

		private float nextRefresh;

		private GUIStyle style;

		internal static bool Collecting => ShowOverlay.Value;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0059: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Expected O, but got Unknown
			ShowOverlay = config.Bind<bool>("1 - Measure", "ShowOverlay", false, "Show frame time and what each feature is doing in the top left of the screen. Nothing is measured while it's hidden.");
			ShowOverlay.SettingChanged += delegate
			{
				startFreshRequested = ShowOverlay.Value;
			};
			overlayKey = config.Bind<KeyboardShortcut>("1 - Measure", "OverlayKey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Shows or hides the overlay.");
			compareKey = config.Bind<KeyboardShortcut>("1 - Measure", "CompareKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Turns every feature off, and back on again, without changing your settings. Use it with the overlay to see what the mod gains you.");
			measureSeconds = config.Bind<float>("1 - Measure", "MeasureSeconds", 3f, new ConfigDescription("How many seconds of frames the overlay averages.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			overlayScale = config.Bind<float>("1 - Measure", "OverlayScale", 1f, new ConfigDescription("Extra size for the overlay on top of the game's own UI scale (screen resolution and the GUI Scale slider in Settings).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			MoveOffPreviousKeys();
		}

		private static void MoveOffPreviousKeys()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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)
			//IL_0038: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (((object)overlayKey.Value/*cast due to .constrained prefix*/).Equals((object)new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>())) && ((object)compareKey.Value/*cast due to .constrained prefix*/).Equals((object)new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>())))
			{
				overlayKey.Value = new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>());
				compareKey.Value = new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>());
			}
		}

		private void Update()
		{
			HandleKeys();
			TickFeatures();
			if (Collecting)
			{
				if (startFreshRequested)
				{
					startFreshRequested = false;
					StartFresh();
				}
				history.Record(Time.unscaledDeltaTime);
				if (!(Time.unscaledTime < nextRefresh))
				{
					nextRefresh = Time.unscaledTime + 0.5f;
					RollFeatureStats();
					RebuildText();
				}
			}
		}

		private static void HandleKeys()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = overlayKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ShowOverlay.Value = !ShowOverlay.Value;
			}
			value = compareKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				Features.AllOff = !Features.AllOff;
				ShowMessage(Features.AllOff ? "Performant World: all features OFF" : "Performant World: features back ON");
			}
		}

		private void StartFresh()
		{
			history.Clear();
			ObjectChurn.StartFresh();
			shownText = "";
			nextRefresh = 0f;
		}

		private static void TickFeatures()
		{
			Features.RunSafely(GpuInstancing.Feature, GpuInstancing.Sync);
			Features.RunSafely(FogCulling.Feature, FogCulling.Sync);
			Features.RunSafely(ObjectFadeIn.Feature, ObjectFadeIn.Sync);
			Features.RunSafely(SmoothLoading.Feature, SmoothLoading.Sync);
			Features.RunSafely(TerrainCache.Feature, TerrainCache.ReleaseDroppedMeshes);
		}

		private static void RollFeatureStats()
		{
			ObjectChurn.Roll();
			GrassCache.RollStats();
			TerrainCache.RollStats();
			SettledStructures.RollStats();
			MinimapUploads.RollStats();
			DistantLightFlicker.RollStats();
			RestingSmoke.RollStats();
			FarAi.RollStats();
		}

		private static void ShowMessage(string message)
		{
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)1, message, 0, (Sprite)null, false, true);
			}
		}

		private void RebuildText()
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			float value = measureSeconds.Value;
			history.Summarize(value);
			text.Length = 0;
			text.Append("Performant World");
			if (Features.AllOff)
			{
				text.Append("   [ALL OFF]");
			}
			text.Append('\n');
			text.AppendFormat("{0:0} fps   {1:0.0} ms avg   {2:0.0} ms worst 1%   ({3:0}s)\n", history.FramesPerSecond, history.AverageMilliseconds, history.WorstMilliseconds, value);
			text.Append(ObjectChurn.Describe()).Append('\n');
			foreach (Feature item in Features.All)
			{
				text.Append(item.OverlayLine).Append('\n');
			}
			text.Append(overlayKey.Value).Append(" hide   ").Append(compareKey.Value)
				.Append(" all on/off   console: ")
				.Append("perf");
			shownText = text.ToString();
		}

		private void OnGUI()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_0060: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_0077: Unknown result type (might be due to invalid IL or missing references)
			if (ShowOverlay.Value)
			{
				if (style == null)
				{
					style = CreateStyle();
				}
				Matrix4x4 matrix = GUI.matrix;
				float num = GameUiScale.Current() * overlayScale.Value;
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				Vector2 val = style.CalcSize(new GUIContent(shownText));
				GUI.Box(new Rect(10f, 10f, val.x, val.y), shownText, style);
				GUI.matrix = matrix;
			}
		}

		private static GUIStyle CreateStyle()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: 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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			GUIStyle val = new GUIStyle(GUI.skin.box)
			{
				alignment = (TextAnchor)0,
				fontSize = 14,
				richText = false,
				padding = new RectOffset(8, 8, 6, 6)
			};
			val.normal.textColor = Color.white;
			return val;
		}
	}
	internal static class MinimapUploads
	{
		private const string Section = "2 - Minimap";

		private const float DefaultInterval = 5f;

		private const float MinInterval = 1f;

		private const float MaxInterval = 30f;

		private static readonly FieldRef<Minimap, Texture2D> FogTexture = AccessTools.FieldRefAccess<Minimap, Texture2D>("m_fogTexture");

		internal static Feature Feature;

		private static ConfigEntry<float> uploadInterval;

		private static bool uploadWaiting;

		private static float nextUploadTime;

		private static int uploadsHeldBack;

		private static int uploadsHeldBackShown;

		private static int applyCallsReplaced;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("2 - Minimap", "Enabled", true, "While you explore, the game re-uploads the whole fog of war texture every time it reveals more map, which causes small stutters. This uploads it at most once per interval, and right away while the big map is open.");
			uploadInterval = config.Bind<float>("2 - Minimap", "UploadInterval", 5f, new ConfigDescription("Seconds between fog of war uploads while exploring. The small minimap catches up at most this late.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			Feature = Features.Register("minimap", "Minimap uploads", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Minimap), "Explore", new Type[2]
			{
				typeof(Vector3),
				typeof(float)
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Minimap), "Update", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(MinimapUploads), "ReplaceTextureUpload", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(MinimapUploads), "AfterMinimapUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return applyCallsReplaced > 0;
		}

		private static IEnumerable<CodeInstruction> ReplaceTextureUpload(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo textureApply = AccessTools.Method(typeof(Texture2D), "Apply", Type.EmptyTypes, (Type[])null);
			MethodInfo requestUpload = AccessTools.Method(typeof(MinimapUploads), "RequestUpload", (Type[])null, (Type[])null);
			foreach (CodeInstruction instruction in instructions)
			{
				if (textureApply != null && CodeInstructionExtensions.Calls(instruction, textureApply))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = requestUpload;
					applyCallsReplaced++;
				}
				yield return instruction;
			}
		}

		private static void RequestUpload(Texture2D fogTexture)
		{
			if (!Feature.IsActive || Time.time >= nextUploadTime)
			{
				Upload(fogTexture);
				return;
			}
			uploadWaiting = true;
			if (MeasureOverlay.Collecting)
			{
				uploadsHeldBack++;
			}
		}

		private static void AfterMinimapUpdate(Minimap __instance)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (uploadWaiting)
			{
				bool flag = (int)__instance.m_mode == 2;
				if (!Feature.IsActive || flag || Time.time >= nextUploadTime)
				{
					Upload(FogTexture.Invoke(__instance));
				}
			}
		}

		private static void Upload(Texture2D fogTexture)
		{
			uploadWaiting = false;
			nextUploadTime = Time.time + uploadInterval.Value;
			if ((Object)(object)fogTexture != (Object)null)
			{
				fogTexture.Apply();
			}
		}

		internal static void RollStats()
		{
			uploadsHeldBackShown = uploadsHeldBack;
			uploadsHeldBack = 0;
		}

		private static string Describe()
		{
			if (uploadsHeldBackShown > 0)
			{
				return "held back " + uploadsHeldBackShown + " uploads";
			}
			if (!uploadWaiting)
			{
				return "";
			}
			return "upload waiting";
		}
	}
	internal static class ObjectChurn
	{
		private const float WindowSeconds = 2f;

		private const int NoCountYet = -1;

		private static int createdInWindow;

		private static int loadedAtWindowStart = -1;

		private static float windowEnd;

		private static float createdPerSecond;

		private static float removedPerSecond;

		private static int loadedNow;

		internal static bool Patch(Harmony harmony)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ZNetScene), "CreateObject", new Type[1] { typeof(ZDO) }, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(GameObject))
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ObjectChurn), "AfterCreateObject", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void AfterCreateObject(GameObject __result)
		{
			if (MeasureOverlay.Collecting && (Object)(object)__result != (Object)null)
			{
				createdInWindow++;
			}
		}

		internal static void Roll()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null)
			{
				loadedAtWindowStart = -1;
				return;
			}
			loadedNow = instance.NrOfInstances();
			if (loadedAtWindowStart == -1)
			{
				StartWindow();
			}
			if (!(Time.unscaledTime < windowEnd))
			{
				int num = createdInWindow - (loadedNow - loadedAtWindowStart);
				createdPerSecond = (float)createdInWindow / 2f;
				removedPerSecond = (float)Mathf.Max(0, num) / 2f;
				StartWindow();
			}
		}

		internal static void StartFresh()
		{
			loadedAtWindowStart = -1;
			createdPerSecond = 0f;
			removedPerSecond = 0f;
		}

		private static void StartWindow()
		{
			loadedAtWindowStart = loadedNow;
			createdInWindow = 0;
			windowEnd = Time.unscaledTime + 2f;
		}

		internal static string Describe()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				return "Objects: no world loaded";
			}
			return $"Objects: {createdPerSecond:0} created/s   {removedPerSecond:0} removed/s   {loadedNow} loaded";
		}
	}
	internal static class ObjectFadeIn
	{
		private const string Section = "12 - Fade In";

		private const float DefaultFadeSeconds = 0.5f;

		private const float MinFadeSeconds = 0.1f;

		private const float MaxFadeSeconds = 2f;

		private const float DefaultMinimumDistance = 20f;

		private const float MinMinimumDistance = 0f;

		private const float MaxMinimumDistance = 100f;

		private const string CrossFadeKeyword = "LOD_FADE_CROSSFADE";

		private static readonly Dictionary<Shader, bool> shaderCanFade = new Dictionary<Shader, bool>();

		internal static Feature Feature;

		private static ConfigEntry<float> fadeSeconds;

		private static ConfigEntry<float> minimumDistance;

		private static float gameFadeSeconds;

		internal static float FadeSeconds => LODGroup.crossFadeAnimationDuration;

		private static bool IsLoading
		{
			get
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null))
				{
					return ((Character)localPlayer).IsTeleporting();
				}
				return true;
			}
		}

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("12 - Fade In", "Enabled", true, "Objects the game loads in as you move around, like body piles, trees, rocks, buildings and creatures, fade in instead of popping in. Objects made of materials that can't fade, mostly small items, still appear at once.");
			fadeSeconds = config.Bind<float>("12 - Fade In", "FadeSeconds", 0.5f, new ConfigDescription("How long the fade takes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			minimumDistance = config.Bind<float>("12 - Fade In", "MinimumDistance", 20f, new ConfigDescription("Objects that load closer than this many metres appear at once, like the game does.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			Feature = Features.Register("fadein", "Fade in", enabled, Describe);
			gameFadeSeconds = LODGroup.crossFadeAnimationDuration;
		}

		internal static bool Patch(Harmony harmony)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ZNetScene), "CreateObject", new Type[1] { typeof(ZDO) }, (Type[])null);
			if (methodInfo == null || methodInfo.ReturnType != typeof(GameObject))
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ObjectFadeIn), "AfterCreateObject", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		internal static void Sync()
		{
			float num = (Feature.IsActive ? fadeSeconds.Value : gameFadeSeconds);
			if (!Mathf.Approximately(LODGroup.crossFadeAnimationDuration, num))
			{
				LODGroup.crossFadeAnimationDuration = num;
			}
		}

		private static void AfterCreateObject(GameObject __result)
		{
			//IL_0038: 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_003e: 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)
			if (Feature.IsActive && !((Object)(object)__result == (Object)null) && !IsLoading && CameraPosition.TryGet(out var position))
			{
				float value = minimumDistance.Value;
				Vector3 val = __result.transform.position - position;
				if (!(((Vector3)(ref val)).sqrMagnitude < value * value) && !((Object)(object)__result.GetComponentInChildren<LodFadeInOut>() != (Object)null))
				{
					__result.AddComponent<FadingInObject>();
				}
			}
		}

		internal static bool CanFade(Renderer renderer)
		{
			Material[] sharedMaterials = renderer.sharedMaterials;
			foreach (Material val in sharedMaterials)
			{
				if ((Object)(object)val == (Object)null || !CanFade(val.shader))
				{
					return false;
				}
			}
			return true;
		}

		private static bool CanFade(Shader shader)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)shader == (Object)null)
			{
				return false;
			}
			if (!shaderCanFade.TryGetValue(shader, out var value))
			{
				LocalKeywordSpace keywordSpace = shader.keywordSpace;
				LocalKeyword val = ((LocalKeywordSpace)(ref keywordSpace)).FindKeyword("LOD_FADE_CROSSFADE");
				value = ((LocalKeyword)(ref val)).isValid;
				shaderCanFade[shader] = value;
			}
			return value;
		}

		private static string Describe()
		{
			int num = Object.FindObjectsByType<FadingInObject>((FindObjectsSortMode)0).Length;
			if (num <= 0)
			{
				return "";
			}
			return num + " objects fading in";
		}
	}
	internal sealed class FadingInObject : MonoBehaviour
	{
		private sealed class HiddenGroup
		{
			internal LODGroup Group;

			internal Vector3 OriginalReferencePoint;
		}

		private const float ShortestDelaySeconds = 0.1f;

		private const float LongestDelaySeconds = 0.3f;

		private const float CleanupMarginSeconds = 0.1f;

		private const float SmallestVisibleScreenShare = 0.0001f;

		private static readonly Vector3 HiddenReferencePoint = new Vector3(987654f, 987654f, 987654f);

		private readonly List<HiddenGroup> hiddenGroups = new List<HiddenGroup>();

		private LODGroup temporaryGroup;

		private float showTime;

		private float finishTime;

		private bool shown;

		private void Awake()
		{
			LODGroup[] array = ((Component)this).GetComponentsInChildren<LODGroup>();
			if (array.Length == 0)
			{
				temporaryGroup = CreateTemporaryGroup();
				if ((Object)(object)temporaryGroup != (Object)null)
				{
					array = (LODGroup[])(object)new LODGroup[1] { temporaryGroup };
				}
			}
			LODGroup[] array2 = array;
			foreach (LODGroup val in array2)
			{
				Hide(val);
			}
			if (hiddenGroups.Count == 0)
			{
				RemoveTemporaryGroup();
				Object.Destroy((Object)(object)this);
			}
			else
			{
				showTime = Time.time + Random.Range(0.1f, 0.3f);
			}
		}

		private void Update()
		{
			if (!shown)
			{
				if (!(Time.time < showTime))
				{
					Show();
				}
			}
			else if (!(Time.time < finishTime))
			{
				RemoveTemporaryGroup();
				Object.Destroy((Object)(object)this);
			}
		}

		private LODGroup CreateTemporaryGroup()
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			List<Renderer> list = new List<Renderer>();
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>();
			foreach (Renderer val in componentsInChildren)
			{
				if ((val is MeshRenderer || val is SkinnedMeshRenderer) && ObjectFadeIn.CanFade(val))
				{
					list.Add(val);
				}
			}
			if (list.Count == 0)
			{
				return null;
			}
			LODGroup val2 = ((Component)this).gameObject.AddComponent<LODGroup>();
			val2.SetLODs((LOD[])(object)new LOD[1]
			{
				new LOD(0.0001f, list.ToArray())
			});
			val2.RecalculateBounds();
			return val2;
		}

		private void Hide(LODGroup group)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)group == (Object)null) && !(group.localReferencePoint == HiddenReferencePoint))
			{
				group.fadeMode = (LODFadeMode)1;
				group.animateCrossFading = true;
				hiddenGroups.Add(new HiddenGroup
				{
					Group = group,
					OriginalReferencePoint = group.localReferencePoint
				});
				group.localReferencePoint = HiddenReferencePoint;
			}
		}

		private void Show()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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)
			foreach (HiddenGroup hiddenGroup in hiddenGroups)
			{
				if ((Object)(object)hiddenGroup.Group != (Object)null && hiddenGroup.Group.localReferencePoint == HiddenReferencePoint)
				{
					hiddenGroup.Group.localReferencePoint = hiddenGroup.OriginalReferencePoint;
				}
			}
			shown = true;
			finishTime = Time.time + ObjectFadeIn.FadeSeconds + 0.1f;
		}

		private void RemoveTemporaryGroup()
		{
			if (!((Object)(object)temporaryGroup == (Object)null))
			{
				Object.Destroy((Object)(object)temporaryGroup);
				temporaryGroup = null;
			}
		}
	}
	internal static class PerfCommand
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__Run;

			public static ConsoleOptionsFetcher <1>__TargetNames;
		}

		internal const string Name = "perf";

		private const string Description = "perf [overlay | shaders | all on|off | <feature> on|off] - Performant World: show the overlay, list shaders, or switch features to measure them";

		private const string OverlayTarget = "overlay";

		private const string AllTarget = "all";

		private const string ShadersTarget = "shaders";

		private const string On = "on";

		private const string Toggle = "toggle";

		private const int TargetArgument = 1;

		private const int ValueArgument = 2;

		internal static bool Patch(Harmony harmony)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Terminal), "InitTerminal", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(PerfCommand), "Register", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static void Register()
		{
			//IL_001a: 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_0025: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			object obj = <>O.<0>__Run;
			if (obj == null)
			{
				ConsoleEvent val = Run;
				<>O.<0>__Run = val;
				obj = (object)val;
			}
			object obj2 = <>O.<1>__TargetNames;
			if (obj2 == null)
			{
				ConsoleOptionsFetcher val2 = TargetNames;
				<>O.<1>__TargetNames = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("perf", "perf [overlay | shaders | all on|off | <feature> on|off] - Performant World: show the overlay, list shaders, or switch features to measure them", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, false, false, false);
		}

		private static void Run(ConsoleEventArgs args)
		{
			if (args.Length <= 1)
			{
				ListFeatures(args.Context);
				return;
			}
			string text = args[1].ToLowerInvariant();
			string value = ((args.Length > 2) ? args[2].ToLowerInvariant() : "toggle");
			switch (text)
			{
			case "overlay":
				MeasureOverlay.ShowOverlay.Value = Resolve(value, MeasureOverlay.ShowOverlay.Value);
				return;
			case "shaders":
				ShaderReport.Print(args.Context);
				return;
			case "all":
				Features.AllOff = !Resolve(value, !Features.AllOff);
				args.Context.AddString(Features.AllOff ? "All features off." : "Features back on.");
				return;
			}
			Feature feature = Features.Find(text);
			if (feature == null)
			{
				args.Context.AddString("Features: " + string.Join(", ", FeatureKeys()));
				return;
			}
			feature.Enabled.Value = Resolve(value, feature.Enabled.Value);
			args.Context.AddString(feature.Label + ": " + feature.Status);
		}

		private static bool Resolve(string value, bool current)
		{
			if (!(value == "toggle"))
			{
				return value == "on";
			}
			return !current;
		}

		private static void ListFeatures(Terminal context)
		{
			foreach (Feature item in Features.All)
			{
				context.AddString(item.Key + ": " + item.Status);
			}
			context.AddString("all off for comparison: " + (Features.AllOff ? "yes" : "no"));
		}

		private static List<string> FeatureKeys()
		{
			List<string> list = new List<string>();
			foreach (Feature item in Features.All)
			{
				list.Add(item.Key);
			}
			return list;
		}

		private static List<string> TargetNames()
		{
			List<string> list = new List<string>();
			list.Add("overlay");
			list.Add("shaders");
			list.Add("all");
			list.AddRange(FeatureKeys());
			return list;
		}
	}
	[BepInPlugin("com.dreich.performantworld", "Performant World", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class PerformantWorldPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.dreich.performantworld";

		public const string Name = "Performant World";

		public const string Version = "1.0.1";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Expected O, but got Unknown
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Expected O, but got Unknown
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Expected O, but got Unknown
			//IL_0253: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			MeasureOverlay.BindSettings(((BaseUnityPlugin)this).Config);
			GrassCache.BindSettings(((BaseUnityPlugin)this).Config);
			TerrainCache.BindSettings(((BaseUnityPlugin)this).Config);
			SettledStructures.BindSettings(((BaseUnityPlugin)this).Config);
			MinimapUploads.BindSettings(((BaseUnityPlugin)this).Config);
			DistantLightFlicker.BindSettings(((BaseUnityPlugin)this).Config);
			RestingSmoke.BindSettings(((BaseUnityPlugin)this).Config);
			FarAi.BindSettings(((BaseUnityPlugin)this).Config);
			SmoothLoading.BindSettings(((BaseUnityPlugin)this).Config);
			GpuInstancing.BindSettings(((BaseUnityPlugin)this).Config);
			FogCulling.BindSettings(((BaseUnityPlugin)this).Config);
			ObjectFadeIn.BindSettings(((BaseUnityPlugin)this).Config);
			if (FiresGhettoNetworking.IsInstalled)
			{
				FiresGhettoNetworking.HandOver(FarAi.Feature, SettledStructures.Feature, SmoothLoading.Feature);
			}
			Harmony val = new Harmony("com.dreich.performantworld");
			PatchUnlessReplacedModInstalled(val, GrassCache.Feature, "com.dreich.performantgrass", "PerformantGrass", GrassCache.Patch);
			PatchUnlessReplacedModInstalled(val, TerrainCache.Feature, "com.dreich.performantterrain", "PerformantTerrain", TerrainCache.Patch);
			PatchUnlessReplacedModInstalled(val, SettledStructures.Feature, "com.dreich.stablestructures", "StableStructures", SettledStructures.Patch);
			Patch(val, MinimapUploads.Feature, MinimapUploads.Patch);
			Patch(val, DistantLightFlicker.Feature, DistantLightFlicker.Patch);
			Patch(val, RestingSmoke.Feature, RestingSmoke.Patch);
			Patch(val, FarAi.Feature, FarAi.Patch);
			Patch(val, SmoothLoading.Feature, SmoothLoading.Patch);
			Patch(val, ObjectFadeIn.Feature, ObjectFadeIn.Patch);
			if (!ObjectChurn.Patch(val))
			{
				Log.LogWarning((object)"The game has changed; the overlay can't count objects being created.");
			}
			if (!PerfCommand.Patch(val))
			{
				Log.LogWarning((object)"The game has changed; the perf console command isn't available.");
			}
			((Component)this).gameObject.AddComponent<MeasureOverlay>();
		}

		private static void Patch(Harmony harmony, Feature feature, Func<Harmony, bool> patch)
		{
			if (feature.IsAvailable)
			{
				bool flag;
				try
				{
					flag = patch(harmony);
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Setting up " + feature.Label + " failed: " + ex));
					flag = false;
				}
				if (!flag)
				{
					Log.LogWarning((object)("The game has changed; " + feature.Label + " is turned off."));
					feature.MakeUnavailable("unavailable");
				}
			}
		}

		private static void PatchUnlessReplacedModInstalled(Harmony harmony, Feature feature, string oldGuid, string oldName, Func<Harmony, bool> patch)
		{
			if (Chainloader.PluginInfos.ContainsKey(oldGuid))
			{
				Log.LogWarning((object)(oldName + " is still installed, so it keeps handling " + feature.Label + ". Remove it to let Performant World take over."));
				feature.MakeUnavailable("handled by " + oldName + " (remove it)");
			}
			else
			{
				Patch(harmony, feature, patch);
			}
		}
	}
	internal static class ReplacedMods
	{
		internal const string GrassGuid = "com.dreich.performantgrass";

		internal const string GrassName = "PerformantGrass";

		internal const string TerrainGuid = "com.dreich.performantterrain";

		internal const string TerrainName = "PerformantTerrain";

		internal const string StructuresGuid = "com.dreich.stablestructures";

		internal const string StructuresName = "StableStructures";
	}
	internal static class FiresGhettoNetworking
	{
		internal const string Guid = "com.Fire.FiresGhettoNetworkMod";

		private const string HandedOverReason = "FGN installed";

		internal static bool IsInstalled => Chainloader.PluginInfos.ContainsKey("com.Fire.FiresGhettoNetworkMod");

		internal static void HandOver(params Feature[] features)
		{
			for (int i = 0; i < features.Length; i++)
			{
				features[i].MakeUnavailable("FGN installed");
			}
		}
	}
	internal static class RestingSmoke
	{
		private sealed class PuffState
		{
			internal float SecondsSlow;
		}

		private const string Section = "4 - Smoke";

		private const float DefaultRestSpeed = 0.1f;

		private const float MinRestSpeed = 0.01f;

		private const float MaxRestSpeed = 1f;

		private const float DefaultRestSeconds = 1f;

		private const float MinRestSeconds = 0.2f;

		private const float MaxRestSeconds = 10f;

		private const float FadeStarted = 0f;

		private static readonly FieldRef<Smoke, Rigidbody> Body = AccessTools.FieldRefAccess<Smoke, Rigidbody>("m_body");

		private static readonly FieldRef<Smoke, float> SecondsAlive = AccessTools.FieldRefAccess<Smoke, float>("m_time");

		private static readonly FieldRef<Smoke, float> FadeSeconds = AccessTools.FieldRefAccess<Smoke, float>("m_fadeTimer");

		private static readonly ConditionalWeakTable<Smoke, PuffState> States = new ConditionalWeakTable<Smoke, PuffState>();

		internal static Feature Feature;

		private static ConfigEntry<float> restSpeed;

		private static ConfigEntry<float> restSeconds;

		private static int restingUpdates;

		private static int framesCounted;

		private static int lastCountedFrame = -1;

		private static float restingPerFrameShown;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("4 - Smoke", "Enabled", true, "Smoke puffs that have come to rest, like smoke pooled under a roof, stop being pushed every frame so the physics engine can let them sleep. They keep their colliders, so smoke damage, fires choking in closed rooms and roofs holding smoke all work as before, and they still fade on time. Anything that bumps a resting puff wakes it up.");
			restSpeed = config.Bind<float>("4 - Smoke", "RestSpeed", 0.1f, new ConfigDescription("A puff moving slower than this (metres per second) counts as resting.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
			restSeconds = config.Bind<float>("4 - Smoke", "RestTime", 1f, new ConfigDescription("Seconds a puff has to stay that slow before it's left to sleep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.2f, 10f), Array.Empty<object>()));
			Feature = Features.Register("smoke", "Resting smoke", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Smoke), "CustomUpdate", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return false;
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(RestingSmoke), "BeforeSmokeUpdate", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}

		private static bool BeforeSmokeUpdate(Smoke __instance, float deltaTime)
		{
			if (!Feature.IsActive)
			{
				return true;
			}
			Rigidbody val = Body.Invoke(__instance);
			if ((Object)(object)val == (Object)null || val.useGravity)
			{
				return true;
			}
			if (!HasBeenSlowLongEnough(__instance, val, deltaTime))
			{
				return true;
			}
			if (MeasureOverlay.Collecting)
			{
				CountRestingPuff();
			}
			AgeWithoutPushing(__instance, deltaTime);
			return false;
		}

		private static bool HasBeenSlowLongEnough(Smoke puff, Rigidbody body, float deltaTime)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			PuffState orCreateValue = States.GetOrCreateValue(puff);
			float value = restSpeed.Value;
			Vector3 linearVelocity = body.linearVelocity;
			if (((Vector3)(ref linearVelocity)).sqrMagnitude > value * value)
			{
				orCreateValue.SecondsSlow = 0f;
				return false;
			}
			orCreateValue.SecondsSlow += deltaTime;
			return orCreateValue.SecondsSlow >= restSeconds.Value;
		}

		private static void AgeWithoutPushing(Smoke puff, float deltaTime)
		{
			ref float reference = ref SecondsAlive.Invoke(puff);
			reference += deltaTime;
			ref float reference2 = ref FadeSeconds.Invoke(puff);
			bool flag = reference2 >= 0f;
			if (reference > puff.m_ttl && !flag)
			{
				puff.StartFadeOut();
			}
			if (!(reference2 < 0f))
			{
				reference2 += deltaTime;
				if (reference2 >= puff.m_fadetime)
				{
					Object.Destroy((Object)(object)((Component)puff).gameObject);
				}
			}
		}

		private static void CountRestingPuff()
		{
			if (lastCountedFrame != Time.frameCount)
			{
				lastCountedFrame = Time.frameCount;
				framesCounted++;
			}
			restingUpdates++;
		}

		internal static void RollStats()
		{
			restingPerFrameShown = ((framesCounted > 0) ? ((float)restingUpdates / (float)framesCounted) : 0f);
			restingUpdates = 0;
			framesCounted = 0;
		}

		private static string Describe()
		{
			int totalSmoke = Smoke.GetTotalSmoke();
			if (totalSmoke == 0)
			{
				return "";
			}
			return $"{totalSmoke} puffs, {Mathf.Min((float)totalSmoke, restingPerFrameShown):0} resting";
		}
	}
	internal static class SettledStructures
	{
		private sealed class PieceState
		{
			internal Vector3Int Cell;

			internal int UnchangedChecks;

			internal bool Settled;

			internal float NextSafetyCheck;

			internal float LastCheck = -1f;

			internal float SupportBefore;
		}

		private const string Section = "10 - Structures";

		private const int DefaultSettleChecks = 5;

		private const int MinSettleChecks = 2;

		private const int MaxSettleChecks = 60;

		private const float DefaultRecheckSeconds = 60f;

		private const float MinRecheckSeconds = 10f;

		private const float MaxRecheckSeconds = 600f;

		private const float DefaultWakeRadius = 8f;

		private const float MinWakeRadius = 2f;

		private const float MaxWakeRadius = 32f;

		private const float GridCellSize = 8f;

		private const float AwaySeconds = 5f;

		private const float RecheckSpreadLow = 0.75f;

		private const float RecheckSpreadHigh = 1.25f;

		private const float NeverChecked = -1f;

		private const float StatsWindowSeconds = 10f;

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

		private static readonly Func<WearNTear, bool> HasSupport = AccessTools.MethodDelegate<Func<WearNTear, bool>>(AccessTools.Method(typeof(WearNTear), "HaveSupport", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Type[] PatchClasses = new Type[4]
		{
			typeof(WearNTearAwakePatch),
			typeof(WearNTearDestroyPatch),
			typeof(WearNTearClearCachePatch),
			typeof(WearNTearUpdateSupportPatch)
		};

		private static readonly Dictionary<WearNTear, PieceState> states = new Dictionary<WearNTear, PieceState>();

		private static readonly Dictionary<Vector3Int, List<WearNTear>> grid = new Dictionary<Vector3Int, List<WearNTear>>();

		private static readonly Stopwatch checkTime = new Stopwatch();

		internal static Feature Feature;

		private static ConfigEntry<int> settleChecks;

		private static ConfigEntry<float> recheckSeconds;

		private static ConfigEntry<float> wakeRadius;

		private static WearNTear pieceBeingChecked;

		private static int checksRun;

		private static int checksSkipped;

		private static int checksRunShown;

		private static int checksSkippedShown;

		private static double checkMillisecondsShown;

		private static float nextStatsWindow;

		internal static void BindSettings(ConfigFile config)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			ConfigEntry<bool> enabled = config.Bind<bool>("10 - Structures", "Enabled", true, "Let building pieces whose support hasn't changed stop re-checking it until something nearby changes. Saves a good amount in large bases. Not needed with Valheim Community Patch, which already does this. (Formerly StableStructures.)");
			settleChecks = config.Bind<int>("10 - Structures", "SettleChecks", 5, new ConfigDescription("How many support checks in a row must come out unchanged before a piece settles.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 60), Array.Empty<object>()));
			recheckSeconds = config.Bind<float>("10 - Structures", "RecheckSeconds", 60f, new ConfigDescription("A settled piece still re-checks its support about this often, as a safety net.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), Array.Empty<object>()));
			wakeRadius = config.Bind<float>("10 - Structures", "WakeRadius", 8f, new ConfigDescription("Building, destroying or a support change wakes settled pieces within this many meters.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 32f), Array.Empty<object>()));
			Feature = Features.Register("structures", "Settled structures", enabled, Describe);
		}

		internal static bool Patch(Harmony harmony)
		{
			Type[] patchClasses = PatchClasses;
			foreach (Type type in patchClasses)
			{
				harmony.CreateClassProcessor(type).Patch();
			}
			return true;
		}

		private static Vector3Int CellOf(Vector3 position)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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 Vector3Int(Mathf.FloorToInt(position.x / 8f), Mathf.FloorToInt(position.y / 8f), Mathf.FloorToInt(position.z / 8f));
		}

		internal static void Register(WearNTear piece)
		{
			//IL_001a: 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_0024: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!states.ContainsKey(piece))
			{
				PieceState pieceState = new PieceState
				{
					Cell = CellOf(((Component)piece).transform.position)
				};
				states[piece] = pieceState;
				PiecesInCell(pieceState.Cell).Add(piece);
				WakeAround(((Component)piece).transform.position, piece);
			}
		}

		private static List<WearNTear> PiecesInCell(Vector3Int cell)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (grid.TryGetValue(cell, out var value))
			{
				return value;
			}
			value = new List<WearNTear>();
			grid[cell] = value;
			return value;
		}

		internal static void Unregister(WearNTear piece)
		{
			//IL_0022: 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_0046: Unknown result type (might be due to invalid IL or missing references)
			if (!states.TryGetValue(piece, out var value))
			{
				return;
			}
			states.Remove(piece);
			if (grid.TryGetValue(value.Cell, out var value2))
			{
				value2.Remove(piece);
				if (value2.Count == 0)
				{
					grid.Remove(value.Cell);
				}
			}
			WakeAround(((Component)piece).transform.position, piece);
		}

		internal static void Wake(WearNTear piece)
		{
			if (!((Object)(object)piece == (Object)null) && !((Object)(object)piece == (Object)(object)pieceBeingChecked) && states.TryGetValue(piece, out var value))
			{
				Wake(value);
			}
		}

		private static void Wake(PieceState state)
		{
			state.Settled = false;
			state.UnchangedChecks = 0;
		}

		private static void WakeAround(Vector3 position, WearNTear except)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			float value = wakeRadius.Value;
			float num = value * value;
			Vector3Int val = CellOf(position - Vector3.one * value);
			Vector3Int val2 = CellOf(position + Vector3.one * value);
			for (int i = ((Vector3Int)(ref val)).x; i <= ((Vector3Int)(ref val2)).x; i++)
			{
				for (int j = ((Vector3Int)(ref val)).y; j <= ((Vector3Int)(ref val2)).y; j++)
				{
					for (int k = ((Vector3Int)(ref val)).z; k <= ((Vector3Int)(ref val2)).z; k++)
					{
						if (!grid.TryGetValue(new Vector3Int(i, j, k), out var value2))
						{
							continue;
						}
						foreach (WearNTear item in value2)
						{
							if (!((Object)(object)item == (Object)null) && !((Object)(object)item == (Object)(object)except))
							{
								Vector3 val3 = ((Component)item).transform.position - position;
								if (!(((Vector3)(ref val3)).sqrMagnitude > num) && states.TryGetValue(item, out var value3))
								{
									Wake(value3);
								}
							}
						}
					}
				}
			}
		}

		internal static bool BeforeSupportCheck(WearNTear piece)
		{
			if (!Feature.IsActive || !states.TryGetValue(piece, out var value))
			{
				return true;
			}
			float time = Time.time;
			bool flag = value.LastCheck == -1f || time - value.LastCheck > 5f;
			value.LastCheck = time;
			if (value.Settled && !flag && time < value.NextSafetyCheck)
			{
				if (MeasureOverlay.Collecting)
				{
					checksSkipped++;
				}
				return false;
			}
			value.SupportBefore = SupportOf.Invoke(piece);
			pieceBeingChecked = piece;
			if (MeasureOverlay.Collecting)
			{
				checksRun++;
				checkTime.Start();
			}
			return true;
		}

		internal static void AfterSupportCheck(WearNTear piece)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)pieceBeingChecked != (Object)(object)piece)
			{
				return;
			}
			if (checkTime.IsRunning)
			{
				checkTime.Stop();
			}
			pieceBeingChecked = null;
			if (!states.TryGetValue(piece, out var value))
			{
				return;
			}
			if (!Mathf.Approximately(SupportOf.Invoke(piece), value.SupportBefore))
			{
				Wake(value);
				WakeAround(((Component)piece).transform.position, piece);
				return;
			}
			if (!HasSupport(piece))
			{
				value.UnchangedChecks = 0;
				return;
			}
			value.UnchangedChecks++;
			if (value.UnchangedChecks >= settleChecks.Value)
			{
				value.Settled = true;
				value.NextSafetyCheck = Time.time + recheckSeconds.Value * Random.Range(0.75f, 1.25f);
			}
		}

		internal static void RollStats()
		{
			if (!(Time.unscaledTime < nextStatsWindow))
			{
				nextStatsWindow = Time.unscaledTime + 10f;
				checksRunShown = checksRun;
				checksSkippedShown = checksSkipped;
				checkMillisecondsShown = checkTime.Elapsed.TotalMilliseconds;
				checksRun = 0;
				checksSkipped = 0;
				checkTime.Reset();
			}
		}

		private static string Describe()
		{
			int num = 0;
			foreach (PieceState value in states.Values)
			{
				if (value.Settled)
				{
					num++;
				}
			}
			return $"last {10f:0}s: {checksRunShown} checks ({checkMillisecondsShown:0.0} ms), {checksSkippedShown} skipped; {num} of {states.Count} pieces settled";
		}
	}
	[HarmonyPatch(typeof(WearNTear), "Awake")]
	internal static class WearNTearAwakePatch
	{
		private static void Postfix(WearNTear __instance, ZNetView ___m_nview)
		{
			if ((Object)(object)___m_nview != (Object)null && ___m_nview.GetZDO() != null)
			{
				SettledStructures.Register(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "OnDestroy")]
	internal static class WearNTearDestroyPatch
	{
		private static void Prefix(WearNTear __instance)
		{
			SettledStructures.Unregister(__instance);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ClearCachedSupport")]
	internal static class WearNTearClearCachePatch
	{
		private static void Postfix(WearNTear __instance)
		{
			SettledStructures.Wake(__instance);
		}
	}
	[HarmonyPatch(typeof(WearNTear), "UpdateSupport")]
	internal static class WearNTearUpdateSupportPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(WearNTear __instance)
		{
			return SettledStructures.BeforeSupportCheck(__instance);
		}

		private static Exception Finalizer(WearNTear __instance, Exception __exception)
		{
			SettledStructures.AfterSupportCheck(__instance);
			return __exception;
		}
	}
	internal static class ShaderReport
	{
		private sealed class ShaderUse
		{
			internal Shader Shader;

			internal int Renderers;
		}

		private const string CrossFadeKeyword = "LOD_FADE_CROSSFADE";

		private const int MostShadersListed = 40;

		internal static void Print(Terminal context)
		{
			List<ShaderUse> list = CollectShaderUse();
			list.Sort((ShaderUse first, ShaderUse second) => second.Renderers.CompareTo(first.Renderers));
			int num = 0;
			foreach (ShaderUse item in list)
			{
				if (SupportsCrossFade(item.Shader))
				{
					num++;
				}
			}
			Report(context, $"{list.Count} shaders in use, {num} support fading in:");
			int num2 = 0;
			foreach (ShaderUse item2 in list)
			{
				if (num2++ >= 40)
				{
					break;
				}
				string arg = (SupportsCrossFade(item2.Shader) ? "fades" : "no fade");
				Report(context, $"  {((Object)item2.Shader).name} - {item2.Renderers} renderers - {arg}");
			}
		}

		private static void Report(Terminal context, string line)
		{
			context.AddString(line);
			PerformantWorldPlugin.Log.LogInfo((object)line);
		}

		private static List<ShaderUse> CollectShaderUse()
		{
			Dictionary<Shader, ShaderUse> dictionary = new Dictionary<Shader, ShaderUse>();
			Renderer[] array = Object.FindObjectsByType<Renderer>((FindObjectsSortMode)0);
			foreach (Renderer val in array)
			{
				if (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer))
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMate