Decompiled source of BalrondNatureOptimizer v0.7.7

BalrondNatureOptimizer.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BalrondNatureOptimizer")]
[assembly: AssemblyDescription("Compatibility and performance optimizer for Balrond Amazing Nature 1.3.8")]
[assembly: AssemblyCompany("DragonMotion")]
[assembly: AssemblyProduct("BalrondNatureOptimizer")]
[assembly: AssemblyFileVersion("0.7.7.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.7.7.0")]
namespace DragonMotion.BalrondNatureOptimizer;

[BepInPlugin("dragonmotion.balrondnatureoptimizer", "BalrondNatureOptimizer", "0.7.7")]
[BepInDependency("balrond.astafaraios.BalrondAmazingNature", "1.3.8")]
public sealed class BalrondNatureOptimizerPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "dragonmotion.balrondnatureoptimizer";

	public const string PluginName = "BalrondNatureOptimizer";

	public const string PluginVersion = "0.7.7";

	public const string BalrondGuid = "balrond.astafaraios.BalrondAmazingNature";

	public const string SupportedBalrondVersion = "1.3.8";

	private void Awake()
	{
		OptimizerRuntime.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
		OptimizerRuntime.Apply();
	}

	private void OnDestroy()
	{
		OptimizerRuntime.Shutdown();
	}
}
internal static class OptimizerRuntime
{
	private struct SavedFloat
	{
		internal bool HasValue;

		internal float Value;
	}

	private struct MaterialRenderState
	{
		internal bool Active;

		internal bool RestoreAlphaState;

		internal int RenderQueue;

		internal bool AlphaTestUnderscore;

		internal bool AlphaTestPlain;

		internal SavedFloat Cutoff;

		internal SavedFloat AlphaCutoff;

		internal SavedFloat CutoffThreshold;

		internal SavedFloat Mode;

		internal SavedFloat ZWrite;

		internal SavedFloat SrcBlend;

		internal SavedFloat DstBlend;

		internal SavedFloat Cull;
	}

	private struct ClutterOriginalState
	{
		internal int Amount;

		internal bool Enabled;
	}

	internal sealed class RuntimeOptions
	{
		internal bool Enabled;

		internal bool DisableMonsterClimbing;

		internal bool ReplaceStableHashReflection;

		internal bool DisableServingTray;

		internal bool DisableServingTrayAutoRegistration;

		internal bool ExcludeHeavyFoodDisplays;

		internal bool DisablePoisonGeysers;

		internal bool OptimizeEnabledPoisonGeysers;

		internal int GeyserParticleLimit;

		internal int GeyserMaxNear;

		internal int GeyserMaxTotal;

		internal bool DisableSwampClutter;

		internal bool DisableSwampVisualVegetation;

		internal bool DisableSwampResourceVegetation;

		internal int BalrondClutterDensityPercent;

		internal bool DisableNonInstancedBalrondClutter;

		internal bool OptimizePortalEffects;

		internal int PortalParticleLimit;

		internal bool OptimizeFloatingDebrisEffects;

		internal int FloatingDebrisParticleLimit;

		internal bool ConservativeMaterialStateFix;

		internal bool AggressiveOpaqueMaterialRestore;

		internal bool DisableKnownHeavyPrefabShadows;

		internal bool FixBalrondConstructionsNullHitEffects;

		internal bool SmoothDistantTerrainRebuild;

		internal int DistantTerrainRegionsPerFrame;

		internal bool CacheVanillaZdoSortComparers;

		internal float StaticPhysicsRecheckInterval;

		internal bool TimeSliceTerrainClutterRebuilds;
	}

	private sealed class LegacyValues
	{
		internal HashSet<ConfigDefinition> ExistingDefinitions;

		internal bool Enabled;

		internal bool ReplaceStableHashReflection;

		internal bool EnableServingTrayIntegration;

		internal bool DisableServingTrayAutoRegistration;

		internal bool ExcludeHeavyFoodDisplays;

		internal bool DisablePoisonGeysers;

		internal bool OptimizeEnabledPoisonGeysers;

		internal int GeyserParticleLimit;

		internal int GeyserMaxNear;

		internal int GeyserMaxTotal;

		internal bool DisableSwampClutter;

		internal bool DisableSwampVisualVegetation;

		internal bool DisableSwampResourceVegetation;

		internal int BalrondClutterDensityPercent;

		internal bool DisableNonInstancedBalrondClutter;

		internal bool ConservativeMaterialStateFix;

		internal bool AggressiveOpaqueMaterialRestore;

		internal bool DisableKnownHeavyPrefabShadows;

		internal bool FixBalrondConstructionsNullHitEffects;

		internal bool SmoothDistantTerrainRebuild;

		internal int DistantTerrainRegionsPerFrame;

		internal bool CacheVanillaZdoSortComparers;

		internal float StaticPhysicsRecheckInterval;

		internal bool TimeSliceTerrainClutterRebuilds;
	}

	private sealed class TerrainLodCycle
	{
		internal object[] Entries;

		internal Heightmap[] Heightmaps;

		internal Vector3[] Offsets;

		internal HMBuildData[] Data;

		internal int Count;

		internal int Next;

		internal bool Active;

		internal Vector3 LastPoint;

		internal WorldGenerator World;

		internal IList Source;

		internal TerrainLod Owner;
	}

	private static readonly string[] VegetationNameTokens = new string[12]
	{
		"grass", "flower", "plant", "leaf", "leaves", "bush", "shrub", "heath", "fern", "moss",
		"vine", "branch"
	};

	private const string BalrondConstructionsGuid = "balrond.astafaraios.BalrondConstructions";

	private static readonly HashSet<string> HeavyServingTrayPrefabs = new HashSet<string>(StringComparer.Ordinal) { "GoatStew_bal", "ShrededMeat_bal", "MeatBalls_bal", "Surstromming_bal" };

	private static readonly List<GameObject> EmptyPrefabList = new List<GameObject>(0);

	private static readonly HashSet<string> SwampClutterPrefabs = new HashSet<string>(StringComparer.Ordinal) { "instanced_mushroom_brown_bal", "instanced_shrub_brown_bal", "instanced_brown_grass_short_bal", "instanced_dead_straw_short_bal", "ormbunke_bal" };

	private static readonly HashSet<string> SwampVisualVegetationPrefabs = new HashSet<string>(StringComparer.Ordinal) { "WetTree1_bal", "WetTree2_bal", "WetTree3_bal", "LinedSwampTreeLarge_bal", "LinedSwampTreeLarge2_bal", "Oak_Swamp_bal", "sapling_Swamp_bal" };

	private static readonly HashSet<string> SwampResourceVegetationPrefabs = new HashSet<string>(StringComparer.Ordinal) { "MineRock_Guck_bal", "Pickable_GuckSack_bal" };

	private static readonly HashSet<string> KnownHeavyShadowPrefabs = new HashSet<string>(StringComparer.Ordinal) { "piece_waterwell_bal", "scrapsmelter_ext1_bal", "raw_wood_roof_top_bal", "raw_wood_roof_45d_top_bal", "StoneboundKiln_bal" };

	private static readonly string[] BalrondConstructionsNullHitEffectPrefabs = new string[5] { "core_wood_wall_2_bal", "core_wood_wall_4_bal", "core_wood_wall_corner_bal", "core_wood_wall_deco2_bal", "core_wood_wall_deco4_bal" };

	private static readonly Dictionary<Clutter, ClutterOriginalState> OriginalBalrondClutterStates = new Dictionary<Clutter, ClutterOriginalState>();

	internal const string BalrondGuid = "balrond.astafaraios.BalrondAmazingNature";

	internal const string SupportedBalrondVersion = "1.3.8";

	private static readonly Version SupportedBalrondVersionValue = new Version(1, 3, 8);

	internal static readonly Harmony Patcher = new Harmony("dragonmotion.balrondnatureoptimizer");

	internal static ManualLogSource Log;

	internal static RuntimeOptions Options;

	internal static ConfigEntry<bool> OptimizerEnabled;

	internal static ConfigEntry<bool> DisableMonsterClimbing;

	internal static ConfigEntry<bool> ReplaceStableHashReflection;

	internal static ConfigEntry<bool> EnableServingTrayIntegration;

	internal static ConfigEntry<bool> DisableServingTrayAutoRegistration;

	internal static ConfigEntry<bool> ExcludeHeavyFoodDisplays;

	internal static ConfigEntry<bool> DisablePoisonGeysers;

	internal static ConfigEntry<bool> OptimizeEnabledPoisonGeysers;

	internal static ConfigEntry<int> GeyserParticleLimit;

	internal static ConfigEntry<int> GeyserMaxNear;

	internal static ConfigEntry<int> GeyserMaxTotal;

	internal static ConfigEntry<bool> DisableSwampClutter;

	internal static ConfigEntry<bool> DisableSwampVisualVegetation;

	internal static ConfigEntry<bool> DisableSwampResourceVegetation;

	internal static ConfigEntry<int> BalrondClutterDensityPercent;

	internal static ConfigEntry<bool> DisableNonInstancedBalrondClutter;

	internal static ConfigEntry<bool> OptimizePortalEffects;

	internal static ConfigEntry<int> PortalParticleLimit;

	internal static ConfigEntry<bool> OptimizeFloatingDebrisEffects;

	internal static ConfigEntry<int> FloatingDebrisParticleLimit;

	internal static ConfigEntry<bool> ConservativeMaterialStateFix;

	internal static ConfigEntry<bool> AggressiveOpaqueMaterialRestore;

	internal static ConfigEntry<bool> DisableKnownHeavyPrefabShadows;

	internal static ConfigEntry<bool> FixBalrondConstructionsNullHitEffects;

	internal static ConfigEntry<bool> SmoothDistantTerrainRebuild;

	internal static ConfigEntry<int> DistantTerrainRegionsPerFrame;

	internal static ConfigEntry<bool> CacheVanillaZdoSortComparers;

	internal static ConfigEntry<float> StaticPhysicsRecheckInterval;

	internal static ConfigEntry<bool> TimeSliceTerrainClutterRebuilds;

	private static readonly Dictionary<string, Type> TypeCache = new Dictionary<string, Type>(StringComparer.Ordinal);

	private static readonly Dictionary<Type, Dictionary<string, FieldInfo>> FieldCache = new Dictionary<Type, Dictionary<string, FieldInfo>>();

	private static readonly List<string> StartupIssues = new List<string>();

	private static Assembly _balrondAssembly;

	private static ConfigFile _config;

	private static bool _initialized;

	private static bool _settingsCaptured;

	private static bool _restartWarningLogged;

	private static bool _collectStartupIssues;

	private static Comparison<ZDO> _zNetSceneZdoComparison;

	private static Comparison<ZDO> _serverSendZdoComparison;

	private static Comparison<ZDO> _clientSendZdoComparison;

	private static float _staticPhysicsRecheckInterval;

	private static readonly Dictionary<int, TerrainLodCycle> TerrainLodCycles = new Dictionary<int, TerrainLodCycle>();

	private static FieldInfo _terrainLodHeightmapsField;

	private static FieldInfo _terrainLodTopStateField;

	private static FieldInfo _terrainEntryHeightmapField;

	private static FieldInfo _terrainEntryOffsetField;

	private static FieldInfo _terrainEntryStateField;

	private static int _terrainReadyState;

	private static int _terrainDoneState;

	private static Action<object, int> _terrainEntryStateSetter;

	private static Action<TerrainLod, int> _terrainTopStateSetter;

	private static FieldRef<Heightmap, HMBuildData> _heightmapBuildDataRef;

	private static FieldRef<Heightmap, Biome[]> _heightmapCornerBiomesRef;

	private static bool _terrainLodCompatibilityChecked;

	private static bool _terrainLodSmoothingFaulted;

	private static int _terrainBudgetFrame = -1;

	private static int _terrainRegionsProcessedThisFrame;

	private static void ReplaceShaderPrefix(Material material, string oldShaderName, ref MaterialRenderState __state)
	{
		__state = default(MaterialRenderState);
		if (!Options.ConservativeMaterialStateFix || (Object)(object)material == (Object)null || !ShouldGuardShaderReplacement(((Object)material).name, oldShaderName))
		{
			return;
		}
		MaterialRenderState materialRenderState = new MaterialRenderState
		{
			Active = true,
			RestoreAlphaState = !HasAuthoredAlphaIntent(material),
			RenderQueue = material.renderQueue,
			AlphaTestUnderscore = material.IsKeywordEnabled("_ALPHATEST_ON"),
			AlphaTestPlain = material.IsKeywordEnabled("ALPHATEST_ON")
		};
		if (materialRenderState.RestoreAlphaState)
		{
			CaptureFloat(material, "_Cutoff", ref materialRenderState.Cutoff);
			CaptureFloat(material, "_AlphaCutoff", ref materialRenderState.AlphaCutoff);
			CaptureFloat(material, "_CutoffThreshold", ref materialRenderState.CutoffThreshold);
			if (Options.AggressiveOpaqueMaterialRestore)
			{
				CaptureFloat(material, "_Mode", ref materialRenderState.Mode);
				CaptureFloat(material, "_ZWrite", ref materialRenderState.ZWrite);
				CaptureFloat(material, "_SrcBlend", ref materialRenderState.SrcBlend);
				CaptureFloat(material, "_DstBlend", ref materialRenderState.DstBlend);
			}
		}
		CaptureFloat(material, "_Cull", ref materialRenderState.Cull);
		__state = materialRenderState;
	}

	private static bool ShouldGuardShaderReplacement(string materialName, string oldShaderName)
	{
		if (string.Equals(oldShaderName, "Balrond/Piece", StringComparison.Ordinal) || string.Equals(oldShaderName, "Balrond/Creature", StringComparison.Ordinal))
		{
			return !LooksLikeVegetation(materialName, oldShaderName);
		}
		return false;
	}

	private static void CaptureFloat(Material material, string property, ref SavedFloat saved)
	{
		if (material.HasProperty(property))
		{
			saved.HasValue = true;
			saved.Value = material.GetFloat(property);
		}
	}

	private static void ReplaceShaderPostfix(Material material, bool __result, MaterialRenderState __state)
	{
		if (!__result || !__state.Active || (Object)(object)material == (Object)null)
		{
			return;
		}
		if (__state.RestoreAlphaState)
		{
			RestoreFloat(material, "_Cutoff", __state.Cutoff, 0f);
			RestoreFloat(material, "_AlphaCutoff", __state.AlphaCutoff, 0f);
			RestoreFloat(material, "_CutoffThreshold", __state.CutoffThreshold, 0f);
			if (Options.AggressiveOpaqueMaterialRestore)
			{
				RestoreFloat(material, "_Mode", __state.Mode, 0f);
				RestoreFloat(material, "_ZWrite", __state.ZWrite, 1f);
				RestoreFloat(material, "_SrcBlend", __state.SrcBlend, 1f);
				RestoreFloat(material, "_DstBlend", __state.DstBlend, 0f);
				if (!__state.AlphaTestUnderscore)
				{
					material.DisableKeyword("_ALPHATEST_ON");
				}
				if (!__state.AlphaTestPlain)
				{
					material.DisableKeyword("ALPHATEST_ON");
				}
				material.renderQueue = __state.RenderQueue;
			}
		}
		RestoreFloat(material, "_Cull", __state.Cull, 2f);
	}

	private static void RestoreFloat(Material material, string property, SavedFloat saved, float fallback)
	{
		if (material.HasProperty(property))
		{
			material.SetFloat(property, saved.HasValue ? saved.Value : fallback);
		}
	}

	private static bool ForceAlphaCutoutFixPrefix(Material material)
	{
		if (!Options.ConservativeMaterialStateFix || (Object)(object)material == (Object)null || (Object)(object)material.shader == (Object)null)
		{
			return true;
		}
		string name = ((Object)material.shader).name;
		if (string.Equals(name, "Custom/StaticRock", StringComparison.Ordinal))
		{
			return false;
		}
		if (!string.Equals(name, "Custom/Piece", StringComparison.Ordinal) && !string.Equals(name, "Custom/Creature", StringComparison.Ordinal))
		{
			return true;
		}
		return LooksLikeVegetation(((Object)material).name, name);
	}

	private static bool HasAuthoredAlphaIntent(Material material)
	{
		if (material.IsKeywordEnabled("_ALPHATEST_ON") || material.IsKeywordEnabled("ALPHATEST_ON") || material.IsKeywordEnabled("_ALPHABLEND_ON") || material.IsKeywordEnabled("ALPHABLEND_ON") || material.IsKeywordEnabled("_ALPHAPREMULTIPLY_ON") || material.IsKeywordEnabled("ALPHAPREMULTIPLY_ON") || material.IsKeywordEnabled("_SURFACE_TYPE_TRANSPARENT"))
		{
			return true;
		}
		if (material.renderQueue >= 2450)
		{
			return true;
		}
		if (material.HasProperty("_Mode"))
		{
			return material.GetFloat("_Mode") >= 0.5f;
		}
		return false;
	}

	private static bool LooksLikeVegetation(string materialName, string shaderName)
	{
		for (int i = 0; i < VegetationNameTokens.Length; i++)
		{
			string value = VegetationNameTokens[i];
			if ((!string.IsNullOrEmpty(materialName) && materialName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) || (!string.IsNullOrEmpty(shaderName) && shaderName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0))
			{
				return true;
			}
		}
		return false;
	}

	private static bool BalrondStableHashPrefix(string value, ref int __result)
	{
		if (value == null)
		{
			__result = 0;
			return false;
		}
		__result = ComputeStableHash(value);
		return false;
	}

	private static int ComputeStableHash(string value)
	{
		int num = 5381;
		int num2 = num;
		for (int i = 0; i < value.Length && value[i] != 0; i += 2)
		{
			num = ((num << 5) + num) ^ value[i];
			if (i == value.Length - 1 || value[i + 1] == '\0')
			{
				break;
			}
			num2 = ((num2 << 5) + num2) ^ value[i + 1];
		}
		return num + num2 * 1566083941;
	}

	private static bool ServingTraySetupPrefix()
	{
		return !Options.DisableServingTray;
	}

	private static bool ServingTrayBuildAutoScanListPrefix(ref List<GameObject> __result)
	{
		if (!Options.DisableServingTrayAutoRegistration)
		{
			return true;
		}
		__result = EmptyPrefabList;
		return false;
	}

	private static bool ServingTrayPreparePrefix(GameObject prefab, bool requireConsumable, ref string failureReason, ref bool __result)
	{
		bool num = Options.DisableServingTrayAutoRegistration && requireConsumable;
		bool flag = (Object)(object)prefab != (Object)null && Options.ExcludeHeavyFoodDisplays && HeavyServingTrayPrefabs.Contains(((Object)prefab).name);
		if (!num && !flag)
		{
			return true;
		}
		failureReason = "blocked by BalrondNatureOptimizer before prefab mutation";
		__result = false;
		return false;
	}

	private static bool MonsterSpawnerSetupPrefix(string name)
	{
		if (Options.DisablePoisonGeysers)
		{
			return !IsPoisonGeyserName(name);
		}
		return true;
	}

	private static void ZNetSceneAwakePostfix(ZNetScene __instance)
	{
		if ((Object)(object)__instance == (Object)null)
		{
			return;
		}
		int num = 0;
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int processedParticleSystems = 0;
		int num5 = 0;
		int num6 = 0;
		int num7 = 0;
		List<string> list = new List<string>();
		if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers)
		{
			GameObject prefab = __instance.GetPrefab("PoisonGeyser_bal");
			GameObject prefab2 = __instance.GetPrefab("PoisonGeyserSpawner_bal");
			if (Options.DisablePoisonGeysers)
			{
				if ((Object)(object)prefab != (Object)null)
				{
					NeutralizePoisonGeyser(prefab);
					num++;
				}
				if ((Object)(object)prefab2 != (Object)null)
				{
					NeutralizePoisonGeyser(prefab2);
					num++;
				}
			}
			else
			{
				if ((Object)(object)prefab != (Object)null)
				{
					OptimizePoisonGeyser(prefab);
					num++;
				}
				if ((Object)(object)prefab2 != (Object)null)
				{
					OptimizePoisonGeyser(prefab2);
					num++;
				}
			}
		}
		bool num8 = !Application.isBatchMode;
		if (num8 && Options.OptimizePortalEffects)
		{
			if (TryOptimizePortal(__instance.GetPrefab("portal_NatureSmall_bal"), 8, "_target_found_red/Particle System/Point light", expectsLightLod: true, Options.PortalParticleLimit, out var processedParticleSystems2))
			{
				num2++;
				num3 += processedParticleSystems2;
			}
			else
			{
				list.Add("portal_NatureSmall_bal");
			}
			if (TryOptimizePortal(__instance.GetPrefab("portal_StoneSmall_bal"), 7, "_target_found_red/Point light", expectsLightLod: false, Options.PortalParticleLimit, out var processedParticleSystems3))
			{
				num2++;
				num3 += processedParticleSystems3;
			}
			else
			{
				list.Add("portal_StoneSmall_bal");
			}
		}
		if (num8 && Options.OptimizeFloatingDebrisEffects)
		{
			if (TryOptimizeFloatingDebris(__instance.GetPrefab("FloatingDebris_bal"), Options.FloatingDebrisParticleLimit, out processedParticleSystems))
			{
				num4 = 1;
			}
			else
			{
				list.Add("FloatingDebris_bal");
			}
		}
		if (num8 && Options.DisableKnownHeavyPrefabShadows)
		{
			foreach (string knownHeavyShadowPrefab in KnownHeavyShadowPrefabs)
			{
				GameObject prefab3 = __instance.GetPrefab(knownHeavyShadowPrefab);
				if (!((Object)(object)prefab3 == (Object)null))
				{
					DisableAllShadows(prefab3);
					num5++;
				}
			}
			GameObject prefab4 = __instance.GetPrefab("FloatingDebris_bal");
			if ((Object)(object)prefab4 != (Object)null)
			{
				DisableCastShadowsOnly(prefab4);
				num5++;
			}
		}
		if (Options.FixBalrondConstructionsNullHitEffects && Chainloader.PluginInfos.ContainsKey("balrond.astafaraios.BalrondConstructions"))
		{
			for (int i = 0; i < BalrondConstructionsNullHitEffectPrefabs.Length; i++)
			{
				GameObject prefab5 = __instance.GetPrefab(BalrondConstructionsNullHitEffectPrefabs[i]);
				int num9 = RemoveEnabledNullEffects((((Object)(object)prefab5 == (Object)null) ? null : prefab5.GetComponent<WearNTear>())?.m_hitEffect);
				if (num9 > 0)
				{
					num6++;
					num7 += num9;
				}
			}
		}
		Log.LogInfo((object)($"Prefab pass: geysers={num}, portals={num2}/2 ({num3} particle systems), " + $"floatingDebris={num4}/1 ({processedParticleSystems} particle system), shadowTemplates={num5}, " + $"constructionRepairs={num6}/{num7}."));
		if (list.Count > 0)
		{
			Warn("Exact 1.3.8 prefab signature mismatch; skipped without partial mutation: " + string.Join(", ", list) + ".");
		}
	}

	private static bool TryOptimizePortal(GameObject prefab, int expectedParticleSystems, string lightPath, bool expectsLightLod, int particleLimit, out int processedParticleSystems)
	{
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		processedParticleSystems = 0;
		if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null || (Object)(object)prefab.GetComponent<TeleportWorld>() == (Object)null)
		{
			return false;
		}
		ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true);
		Transform val = prefab.transform.Find(lightPath);
		Light val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<Light>());
		LightLod val3 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<LightLod>());
		LightFlicker val4 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<LightFlicker>());
		bool flag = ((!expectsLightLod) ? ((Object)(object)val3 == (Object)null && (Object)(object)val4 != (Object)null) : ((Object)(object)val3 != (Object)null && (Object)(object)val4 == (Object)null));
		if (componentsInChildren.Length != expectedParticleSystems || (Object)(object)val2 == (Object)null || !flag)
		{
			return false;
		}
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if ((Object)(object)componentsInChildren[i] == (Object)null)
			{
				return false;
			}
		}
		for (int j = 0; j < componentsInChildren.Length; j++)
		{
			MainModule main = componentsInChildren[j].main;
			((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0;
			if (((MainModule)(ref main)).maxParticles > particleLimit)
			{
				((MainModule)(ref main)).maxParticles = particleLimit;
			}
		}
		val2.shadows = (LightShadows)0;
		if ((Object)(object)val3 != (Object)null)
		{
			val3.m_shadowLod = false;
		}
		processedParticleSystems = componentsInChildren.Length;
		return true;
	}

	private static bool TryOptimizeFloatingDebris(GameObject prefab, int particleLimit, out int processedParticleSystems)
	{
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		processedParticleSystems = 0;
		if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null || (Object)(object)prefab.GetComponent<Floating>() == (Object)null || (Object)(object)prefab.GetComponent<ZSyncTransform>() == (Object)null || (Object)(object)prefab.GetComponent<Rigidbody>() == (Object)null || (Object)(object)prefab.GetComponentInChildren<Container>(true) == (Object)null)
		{
			return false;
		}
		ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true);
		Transform val = prefab.transform.Find("WaterSurface/vfx_water_surface");
		ParticleSystem val2 = (((Object)(object)val == (Object)null) ? null : ((Component)val).GetComponent<ParticleSystem>());
		if (componentsInChildren.Length != 1 || (Object)(object)val2 == (Object)null || (Object)(object)componentsInChildren[0] != (Object)(object)val2)
		{
			return false;
		}
		MainModule main = val2.main;
		((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0;
		if (((MainModule)(ref main)).maxParticles > particleLimit)
		{
			((MainModule)(ref main)).maxParticles = particleLimit;
		}
		processedParticleSystems = 1;
		return true;
	}

	private static int RemoveEnabledNullEffects(EffectList effectList)
	{
		EffectData[] array = effectList?.m_effectPrefabs;
		if (array == null || array.Length == 0)
		{
			return 0;
		}
		int num = 0;
		foreach (EffectData val in array)
		{
			if (val != null && val.m_enabled && (Object)(object)val.m_prefab == (Object)null)
			{
				num++;
			}
		}
		if (num == 0)
		{
			return 0;
		}
		EffectData[] array2 = (EffectData[])(object)new EffectData[array.Length - num];
		int num2 = 0;
		foreach (EffectData val2 in array)
		{
			if (val2 == null || !val2.m_enabled || !((Object)(object)val2.m_prefab == (Object)null))
			{
				array2[num2++] = val2;
			}
		}
		effectList.m_effectPrefabs = array2;
		return num;
	}

	private static void NeutralizePoisonGeyser(GameObject prefab)
	{
		for (int num = prefab.transform.childCount - 1; num >= 0; num--)
		{
			Transform child = prefab.transform.GetChild(num);
			if ((Object)(object)child != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)((Component)child).gameObject);
			}
		}
		Component[] components = prefab.GetComponents<Component>();
		for (int num2 = components.Length - 1; num2 >= 0; num2--)
		{
			Component val = components[num2];
			if (!((Object)(object)val == (Object)null) && !(val is Transform) && !(val is ZNetView))
			{
				Object.DestroyImmediate((Object)(object)val);
			}
		}
	}

	private static void OptimizePoisonGeyser(GameObject prefab)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		ParticleSystem[] componentsInChildren = prefab.GetComponentsInChildren<ParticleSystem>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			MainModule main = componentsInChildren[i].main;
			((MainModule)(ref main)).cullingMode = (ParticleSystemCullingMode)0;
			if (((MainModule)(ref main)).maxParticles > Options.GeyserParticleLimit)
			{
				((MainModule)(ref main)).maxParticles = Options.GeyserParticleLimit;
			}
		}
		Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren<Renderer>(true);
		for (int j = 0; j < componentsInChildren2.Length; j++)
		{
			if (componentsInChildren2[j] is ParticleSystemRenderer)
			{
				componentsInChildren2[j].shadowCastingMode = (ShadowCastingMode)0;
				componentsInChildren2[j].receiveShadows = false;
			}
		}
		Light[] componentsInChildren3 = prefab.GetComponentsInChildren<Light>(true);
		for (int k = 0; k < componentsInChildren3.Length; k++)
		{
			componentsInChildren3[k].shadows = (LightShadows)0;
		}
		SpawnArea[] componentsInChildren4 = prefab.GetComponentsInChildren<SpawnArea>(true);
		for (int l = 0; l < componentsInChildren4.Length; l++)
		{
			if (componentsInChildren4[l].m_maxNear > Options.GeyserMaxNear)
			{
				componentsInChildren4[l].m_maxNear = Options.GeyserMaxNear;
			}
			if (componentsInChildren4[l].m_maxTotal > Options.GeyserMaxTotal)
			{
				componentsInChildren4[l].m_maxTotal = Options.GeyserMaxTotal;
			}
		}
	}

	private static void DisableAllShadows(GameObject prefab)
	{
		Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			componentsInChildren[i].shadowCastingMode = (ShadowCastingMode)0;
			componentsInChildren[i].receiveShadows = false;
		}
	}

	private static void DisableCastShadowsOnly(GameObject prefab)
	{
		Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			componentsInChildren[i].shadowCastingMode = (ShadowCastingMode)0;
		}
	}

	private static void ZoneSystemSetupLocationsPrefix()
	{
		int num = ((GetStaticField("BalrondNature.VegetationBuilder", "ZoneVegetations") is List<ZoneVegetation> list) ? FilterVegetationList(list) : 0);
		if (num > 0)
		{
			Log.LogInfo((object)$"Removed the Swamp biome bit from {num} pending Amazing Nature vegetation definition(s).");
		}
	}

	private static void ZoneSystemSetupLocationsPostfix(ZoneSystem __instance)
	{
		if (!((Object)(object)__instance == (Object)null))
		{
			int num = FilterVegetationList(__instance.m_vegetation);
			if (num > 0)
			{
				Log.LogInfo((object)$"Removed the Swamp biome bit from {num} active Amazing Nature vegetation definition(s).");
			}
		}
	}

	private static int FilterVegetationList(List<ZoneVegetation> list)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		if (list == null)
		{
			return 0;
		}
		int num = 0;
		for (int num2 = list.Count - 1; num2 >= 0; num2--)
		{
			ZoneVegetation val = list[num2];
			if (val != null)
			{
				string vegetationName = GetVegetationName(val);
				if ((Options.DisablePoisonGeysers && IsPoisonGeyserName(vegetationName)) | (Options.DisableSwampVisualVegetation && SwampVisualVegetationPrefabs.Contains(vegetationName)) | (Options.DisableSwampResourceVegetation && SwampResourceVegetationPrefabs.Contains(vegetationName)))
				{
					if ((int)val.m_biome == 0)
					{
						list.RemoveAt(num2);
						num++;
					}
					else if ((val.m_biome & 2) != 0)
					{
						val.m_biome = (Biome)(val.m_biome & -3);
						num++;
						if ((int)val.m_biome == 0)
						{
							list.RemoveAt(num2);
						}
					}
				}
			}
		}
		return num;
	}

	private static void ClutterSystemAwakePrefix()
	{
		if (GetInstanceField(GetStaticField("BalrondNature.Launch", "clutterBuilder"), "clutterObjects") is List<Clutter> list)
		{
			ConfigureBalrondClutter(list, out var scaled, out var disabled);
			int num = (Options.DisableSwampClutter ? FilterClutterList(list) : 0);
			if (scaled > 0 || disabled > 0 || num > 0)
			{
				Log.LogInfo((object)($"Configured pending Amazing Nature clutter: density={Options.BalrondClutterDensityPercent}%, " + $"scaled={scaled}, nonInstancedDisabled={disabled}, swampDefinitionsChanged={num}."));
			}
		}
	}

	private static void ConfigureBalrondClutter(List<Clutter> list, out int scaled, out int disabled)
	{
		scaled = 0;
		disabled = 0;
		if (list == null)
		{
			return;
		}
		int num = Mathf.Clamp(Options.BalrondClutterDensityPercent, 0, 100);
		for (int i = 0; i < list.Count; i++)
		{
			Clutter val = list[i];
			if (val != null)
			{
				if (!OriginalBalrondClutterStates.TryGetValue(val, out var value))
				{
					value = new ClutterOriginalState
					{
						Amount = val.m_amount,
						Enabled = val.m_enabled
					};
					OriginalBalrondClutterStates.Add(val, value);
				}
				int num2 = (val.m_amount = (value.Amount * num + 50) / 100);
				val.m_enabled = value.Enabled && (!Options.DisableNonInstancedBalrondClutter || val.m_instanced);
				if (num2 != value.Amount)
				{
					scaled++;
				}
				if (value.Enabled && !val.m_enabled)
				{
					disabled++;
				}
			}
		}
	}

	private static int FilterClutterList(List<Clutter> list)
	{
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (list == null)
		{
			return 0;
		}
		int num = 0;
		for (int num2 = list.Count - 1; num2 >= 0; num2--)
		{
			Clutter val = list[num2];
			if (val != null && SwampClutterPrefabs.Contains(GetClutterName(val)))
			{
				if ((int)val.m_biome == 0)
				{
					list.RemoveAt(num2);
					num++;
				}
				else if ((val.m_biome & 2) != 0)
				{
					val.m_biome = (Biome)(val.m_biome & -3);
					num++;
					if ((int)val.m_biome == 0)
					{
						list.RemoveAt(num2);
					}
				}
			}
		}
		return num;
	}

	private static string GetVegetationName(ZoneVegetation vegetation)
	{
		if (string.IsNullOrEmpty(vegetation.m_name))
		{
			if (!((Object)(object)vegetation.m_prefab == (Object)null))
			{
				return ((Object)vegetation.m_prefab).name;
			}
			return string.Empty;
		}
		return vegetation.m_name;
	}

	private static string GetClutterName(Clutter clutter)
	{
		if ((Object)(object)clutter.m_prefab != (Object)null && !string.IsNullOrEmpty(((Object)clutter.m_prefab).name))
		{
			return ((Object)clutter.m_prefab).name;
		}
		string text;
		if (string.IsNullOrEmpty(clutter.m_name) || !clutter.m_name.StartsWith("balrond_", StringComparison.Ordinal))
		{
			text = clutter.m_name;
			if (text == null)
			{
				return string.Empty;
			}
		}
		else
		{
			text = clutter.m_name.Substring("balrond_".Length);
		}
		return text;
	}

	private static bool IsPoisonGeyserName(string name)
	{
		if (!string.Equals(name, "PoisonGeyser_bal", StringComparison.Ordinal))
		{
			return string.Equals(name, "PoisonGeyserSpawner_bal", StringComparison.Ordinal);
		}
		return true;
	}

	private static void GameShutdownPostfix()
	{
		ClearCollection(GetStaticField("BalrondNature.WorldGenPatches", "takenNamesCache"));
		ClearCollection(GetStaticField("BalrondNature.WorldGenPatches+ClutterSystem_Awake_Patch", "PatchedClutterSystems"));
		ResetOwnRuntimeState();
		ResetStreamingSessionState();
	}

	private static void ResetOwnRuntimeState()
	{
		RestoreBalrondClutterStates();
	}

	private static void ResetPluginBindings()
	{
		ResetOwnRuntimeState();
		ResetStreamingBindings();
	}

	private static void RestoreBalrondClutterStates()
	{
		foreach (KeyValuePair<Clutter, ClutterOriginalState> originalBalrondClutterState in OriginalBalrondClutterStates)
		{
			if (originalBalrondClutterState.Key != null)
			{
				originalBalrondClutterState.Key.m_amount = originalBalrondClutterState.Value.Amount;
				originalBalrondClutterState.Key.m_enabled = originalBalrondClutterState.Value.Enabled;
			}
		}
		OriginalBalrondClutterStates.Clear();
	}

	internal static void Initialize(ConfigFile config, ManualLogSource logger)
	{
		//IL_0217: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Expected O, but got Unknown
		//IL_0264: Unknown result type (might be due to invalid IL or missing references)
		//IL_026e: Expected O, but got Unknown
		//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bc: Expected O, but got Unknown
		//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bb: Expected O, but got Unknown
		//IL_0464: Unknown result type (might be due to invalid IL or missing references)
		//IL_046e: Expected O, but got Unknown
		//IL_04be: Unknown result type (might be due to invalid IL or missing references)
		//IL_04c8: Expected O, but got Unknown
		//IL_0617: Unknown result type (might be due to invalid IL or missing references)
		//IL_0621: Expected O, but got Unknown
		//IL_06aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b4: Expected O, but got Unknown
		Log = logger;
		if (_initialized)
		{
			return;
		}
		_initialized = true;
		_config = config;
		bool saveOnConfigSet = config.SaveOnConfigSet;
		config.SaveOnConfigSet = false;
		try
		{
			LegacyValues legacyValues = ReadAndRemoveLegacyConfig(config);
			OptimizerEnabled = config.Bind<bool>("01 - General", "Enable Optimizer", true, RestartRequired("Master switch. When false, this plugin installs no patches and changes no prefab templates."));
			ApplyLegacyValue(OptimizerEnabled, legacyValues.Enabled, legacyValues, "General", "Enable Optimizer");
			ReplaceStableHashReflection = config.Bind<bool>("02 - Amazing Nature Runtime", "Replace StableHash Reflection", true, RestartRequired("Replace BalrondHashCompat MethodInfo.Invoke calls with Valheim's exact allocation-free stable-hash algorithm."));
			ApplyLegacyValue(ReplaceStableHashReflection, legacyValues.ReplaceStableHashReflection, legacyValues, "Balrond Status Effects", "Replace StableHash Reflection");
			DisableMonsterClimbing = config.Bind<bool>("02 - Amazing Nature Runtime", "Disable Monster Climbing", false, RestartRequired("Remove Amazing Nature's global monster climbing postfix. Off by default because disabling it changes monster movement around steep terrain and base walls."));
			EnableServingTrayIntegration = config.Bind<bool>("03 - Serving Tray", "Enable Serving Tray Integration", false, RestartRequired("Allow Amazing Nature to mutate food prefabs into build pieces. Off by default because every spawned copy then gains Piece/WearNTear behavior."));
			ApplyLegacyValue(EnableServingTrayIntegration, legacyValues.EnableServingTrayIntegration, legacyValues, "Balrond Serving Tray", "Enable Serving Tray Integration");
			DisableServingTrayAutoRegistration = config.Bind<bool>("03 - Serving Tray", "Disable Automatic Consumable Registration", true, RestartRequired("When Serving Tray is enabled, keep only Amazing Nature's explicit manual entries instead of automatically registering every consumable."));
			ApplyLegacyValue(DisableServingTrayAutoRegistration, legacyValues.DisableServingTrayAutoRegistration, legacyValues, "Balrond Serving Tray", "Disable Automatic Consumable Registration");
			ExcludeHeavyFoodDisplays = config.Bind<bool>("03 - Serving Tray", "Exclude Extremely Heavy Food Displays", true, RestartRequired("Block four audited 196k-243k triangle food displays from new Serving Tray registration."));
			ApplyLegacyValue(ExcludeHeavyFoodDisplays, legacyValues.ExcludeHeavyFoodDisplays, legacyValues, "Balrond Serving Tray", "Exclude Extremely Heavy Food Displays");
			DisablePoisonGeysers = config.Bind<bool>("04 - Swamp", "Disable Poison Geysers", true, RestartRequired("Remove poison geysers from Swamp generation and reduce their registered templates to lightweight network roots."));
			ApplyLegacyValue(DisablePoisonGeysers, legacyValues.DisablePoisonGeysers, legacyValues, "Balrond Swamp", "Disable Poison Geysers");
			OptimizeEnabledPoisonGeysers = config.Bind<bool>("04 - Swamp", "Optimize Poison Geysers When Enabled", true, RestartRequired("When geysers remain enabled, apply particle, light-shadow, and SpawnArea caps once to their exact templates."));
			ApplyLegacyValue(OptimizeEnabledPoisonGeysers, legacyValues.OptimizeEnabledPoisonGeysers, legacyValues, "Balrond Swamp", "Optimize Poison Geysers When Enabled");
			GeyserParticleLimit = config.Bind<int>("04 - Swamp", "Geyser Max Particles Per System", 512, new ConfigDescription(RestartRequired("Maximum particles for each enabled poison-geyser particle system."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(32, 2000), Array.Empty<object>()));
			ApplyLegacyValue(GeyserParticleLimit, legacyValues.GeyserParticleLimit, legacyValues, "Balrond Swamp", "Geyser Max Particles Per System");
			GeyserMaxNear = config.Bind<int>("04 - Swamp", "Geyser Spawner Max Near", 4, new ConfigDescription(RestartRequired("Maximum nearby monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			ApplyLegacyValue(GeyserMaxNear, legacyValues.GeyserMaxNear, legacyValues, "Balrond Swamp", "Geyser Spawner Max Near");
			GeyserMaxTotal = config.Bind<int>("04 - Swamp", "Geyser Spawner Max Total", 12, new ConfigDescription(RestartRequired("Maximum total monsters for PoisonGeyserSpawner_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			ApplyLegacyValue(GeyserMaxTotal, legacyValues.GeyserMaxTotal, legacyValues, "Balrond Swamp", "Geyser Spawner Max Total");
			DisableSwampClutter = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp Clutter", true, RestartRequired("Remove only the Swamp biome bit from five Amazing Nature clutter definitions."));
			ApplyLegacyValue(DisableSwampClutter, legacyValues.DisableSwampClutter, legacyValues, "Balrond Swamp", "Disable Balrond Swamp Clutter");
			DisableSwampVisualVegetation = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp World Vegetation", true, RestartRequired("Remove only the Swamp biome bit from Amazing Nature's extra swamp trees and saplings."));
			ApplyLegacyValue(DisableSwampVisualVegetation, legacyValues.DisableSwampVisualVegetation, legacyValues, "Balrond Swamp", "Disable Balrond Swamp World Vegetation");
			DisableSwampResourceVegetation = config.Bind<bool>("04 - Swamp", "Disable Balrond Swamp Resource Vegetation", false, RestartRequired("Also remove Amazing Nature MineRock_Guck and Pickable_GuckSack generation from Swamp. Off by default to preserve resources."));
			ApplyLegacyValue(DisableSwampResourceVegetation, legacyValues.DisableSwampResourceVegetation, legacyValues, "Balrond Swamp", "Disable Balrond Swamp Resource Vegetation");
			BalrondClutterDensityPercent = config.Bind<int>("05 - Balrond Clutter", "Balrond Clutter Density Percent", 50, new ConfigDescription(RestartRequired("Scale only Amazing Nature clutter placement attempts."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			ApplyLegacyValue(BalrondClutterDensityPercent, legacyValues.BalrondClutterDensityPercent, legacyValues, "Balrond Streaming", "Balrond Clutter Density Percent");
			DisableNonInstancedBalrondClutter = config.Bind<bool>("05 - Balrond Clutter", "Disable Non-Instanced Balrond Clutter", true, RestartRequired("Disable Amazing Nature clutter entries that instantiate separate GameObjects instead of using InstanceRenderer."));
			ApplyLegacyValue(DisableNonInstancedBalrondClutter, legacyValues.DisableNonInstancedBalrondClutter, legacyValues, "Balrond Streaming", "Disable Non-Instanced Balrond Clutter");
			OptimizePortalEffects = config.Bind<bool>("06 - Rendering", "Optimize Portal Effects", true, RestartRequired("On the two exact 1.3.8 small-portal templates, use Automatic particle culling, cap persistent particles, and disable only the portal light's shadows."));
			PortalParticleLimit = config.Bind<int>("06 - Rendering", "Portal Max Particles Per System", 384, new ConfigDescription(RestartRequired("Maximum particles per system on portal_NatureSmall_bal and portal_StoneSmall_bal."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(128, 1000), Array.Empty<object>()));
			OptimizeFloatingDebrisEffects = config.Bind<bool>("06 - Rendering", "Optimize Floating Debris Effects", true, RestartRequired("On FloatingDebris_bal, use Automatic culling and cap only its exact persistent water-surface particle system."));
			FloatingDebrisParticleLimit = config.Bind<int>("06 - Rendering", "Floating Debris Max Particles", 256, new ConfigDescription(RestartRequired("Maximum particles for FloatingDebris_bal/WaterSurface/vfx_water_surface."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 1000), Array.Empty<object>()));
			ConservativeMaterialStateFix = config.Bind<bool>("06 - Rendering", "Conservative Material State Fix", false, RestartRequired("Guard authored cutoff and culling state while Amazing Nature 1.3.8 replaces non-vegetation Piece, Creature, and StaticRock shaders. Off by default because it changes first-render material state."));
			ApplyLegacyValue(ConservativeMaterialStateFix, legacyValues.ConservativeMaterialStateFix, legacyValues, "Balrond Shaders", "Conservative Material State Fix");
			AggressiveOpaqueMaterialRestore = config.Bind<bool>("06 - Rendering", "Aggressive Opaque Material Restore", false, RestartRequired("For materials authored as opaque, also restore alpha keywords, blend/ZWrite state, and render queue after Amazing Nature's broad force-alpha pass. Implies the conservative guard and remains opt-in because misclassified alpha textures can render incorrectly."));
			ApplyLegacyValue(AggressiveOpaqueMaterialRestore, legacyValues.AggressiveOpaqueMaterialRestore, legacyValues, "Balrond Shaders", "Aggressive Opaque Material Restore");
			DisableKnownHeavyPrefabShadows = config.Bind<bool>("06 - Rendering", "Disable Shadows On Audited Heavy Prefabs", false, RestartRequired("Optional visual tradeoff: disable cast/receive shadows on five audited building templates and cast shadows only on FloatingDebris_bal."));
			ApplyLegacyValue(DisableKnownHeavyPrefabShadows, legacyValues.DisableKnownHeavyPrefabShadows, legacyValues, "Balrond Rendering", "Disable Shadows On Known Heavy Prefabs");
			FixBalrondConstructionsNullHitEffects = config.Bind<bool>("07 - Compatibility Fixes", "Fix Balrond Constructions Null Hit Effects", true, RestartRequired("Remove only enabled missing hit-effect references from five affected Balrond Constructions core-wood wall templates."));
			ApplyLegacyValue(FixBalrondConstructionsNullHitEffects, legacyValues.FixBalrondConstructionsNullHitEffects, legacyValues, "Balrond Lifecycle", "Fix Balrond Constructions Null Hit Effects");
			SmoothDistantTerrainRebuild = config.Bind<bool>("08 - Vanilla Streaming", "Smooth Distant Terrain Rebuild", true, RestartRequired("Spread Valheim's 3x3 distant-terrain refresh across rendered frames instead of regenerating all nine regions in one frame."));
			ApplyLegacyValue(SmoothDistantTerrainRebuild, legacyValues.SmoothDistantTerrainRebuild, legacyValues, "Vanilla Streaming", "Smooth Distant Terrain Rebuild");
			DistantTerrainRegionsPerFrame = config.Bind<int>("08 - Vanilla Streaming", "Distant Terrain Regions Per Frame", 1, new ConfigDescription(RestartRequired("Ready distant Heightmap regions regenerated per rendered frame while smoothing is enabled."), (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), Array.Empty<object>()));
			ApplyLegacyValue(DistantTerrainRegionsPerFrame, legacyValues.DistantTerrainRegionsPerFrame, legacyValues, "Vanilla Streaming", "Distant Terrain Regions Per Frame");
			CacheVanillaZdoSortComparers = config.Bind<bool>("08 - Vanilla Streaming", "Cache ZDO Sort Comparers", true, RestartRequired("Replace three repeatedly allocated vanilla Comparison<ZDO> delegates with exact cached delegates."));
			ApplyLegacyValue(CacheVanillaZdoSortComparers, legacyValues.CacheVanillaZdoSortComparers, legacyValues, "Vanilla Streaming", "Cache ZDO Sort Comparers");
			StaticPhysicsRecheckInterval = config.Bind<float>("08 - Vanilla Streaming", "Static Physics Recheck Interval", 1f, new ConfigDescription(RestartRequired("Seconds between ground/solid-height rechecks after a StaticPhysics object becomes eligible. Zero preserves vanilla behavior."), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			ApplyLegacyValue(StaticPhysicsRecheckInterval, legacyValues.StaticPhysicsRecheckInterval, legacyValues, "Vanilla Streaming", "Static Physics Recheck Interval");
			TimeSliceTerrainClutterRebuilds = config.Bind<bool>("08 - Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds", true, RestartRequired("Let Valheim's existing ClutterSystem rebuild at most one missing/reset grass patch per rendered frame after terrain changes."));
			ApplyLegacyValue(TimeSliceTerrainClutterRebuilds, legacyValues.TimeSliceTerrainClutterRebuilds, legacyValues, "Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds");
		}
		catch
		{
			_config = null;
			_initialized = false;
			throw;
		}
		finally
		{
			config.SaveOnConfigSet = saveOnConfigSet;
		}
		if (saveOnConfigSet)
		{
			config.Save();
		}
		_config.SettingChanged += OnConfigSettingChanged;
	}

	private static LegacyValues ReadAndRemoveLegacyConfig(ConfigFile config)
	{
		LegacyValues result = new LegacyValues
		{
			ExistingDefinitions = CaptureExistingConfigDefinitions(config),
			Enabled = TakeLegacy(config, "General", "Enable Optimizer", fallback: true),
			ReplaceStableHashReflection = TakeLegacy(config, "Balrond Status Effects", "Replace StableHash Reflection", fallback: true),
			EnableServingTrayIntegration = TakeLegacy(config, "Balrond Serving Tray", "Enable Serving Tray Integration", fallback: false),
			DisableServingTrayAutoRegistration = TakeLegacy(config, "Balrond Serving Tray", "Disable Automatic Consumable Registration", fallback: true),
			ExcludeHeavyFoodDisplays = TakeLegacy(config, "Balrond Serving Tray", "Exclude Extremely Heavy Food Displays", fallback: true),
			DisablePoisonGeysers = TakeLegacy(config, "Balrond Swamp", "Disable Poison Geysers", fallback: true),
			OptimizeEnabledPoisonGeysers = TakeLegacy(config, "Balrond Swamp", "Optimize Poison Geysers When Enabled", fallback: true),
			GeyserParticleLimit = TakeLegacy(config, "Balrond Swamp", "Geyser Max Particles Per System", 512),
			GeyserMaxNear = TakeLegacy(config, "Balrond Swamp", "Geyser Spawner Max Near", 4),
			GeyserMaxTotal = TakeLegacy(config, "Balrond Swamp", "Geyser Spawner Max Total", 12),
			DisableSwampClutter = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp Clutter", fallback: true),
			DisableSwampVisualVegetation = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp World Vegetation", fallback: true),
			DisableSwampResourceVegetation = TakeLegacy(config, "Balrond Swamp", "Disable Balrond Swamp Resource Vegetation", fallback: false),
			BalrondClutterDensityPercent = TakeLegacy(config, "Balrond Streaming", "Balrond Clutter Density Percent", 50),
			DisableNonInstancedBalrondClutter = TakeLegacy(config, "Balrond Streaming", "Disable Non-Instanced Balrond Clutter", fallback: true),
			ConservativeMaterialStateFix = TakeLegacy(config, "Balrond Shaders", "Conservative Material State Fix", fallback: false),
			AggressiveOpaqueMaterialRestore = TakeLegacy(config, "Balrond Shaders", "Aggressive Opaque Material Restore", fallback: false),
			DisableKnownHeavyPrefabShadows = TakeLegacy(config, "Balrond Rendering", "Disable Shadows On Known Heavy Prefabs", fallback: false),
			FixBalrondConstructionsNullHitEffects = TakeLegacy(config, "Balrond Lifecycle", "Fix Balrond Constructions Null Hit Effects", fallback: true),
			SmoothDistantTerrainRebuild = TakeLegacy(config, "Vanilla Streaming", "Smooth Distant Terrain Rebuild", fallback: true),
			DistantTerrainRegionsPerFrame = TakeLegacy(config, "Vanilla Streaming", "Distant Terrain Regions Per Frame", 1),
			CacheVanillaZdoSortComparers = TakeLegacy(config, "Vanilla Streaming", "Cache ZDO Sort Comparers", fallback: true),
			StaticPhysicsRecheckInterval = TakeLegacy(config, "Vanilla Streaming", "Static Physics Recheck Interval", 1f),
			TimeSliceTerrainClutterRebuilds = TakeLegacy(config, "Vanilla Streaming", "Time-Slice Terrain-Triggered Clutter Rebuilds", fallback: true)
		};
		RemoveLegacy(config, "Balrond Shaders", "Suppress Duplicate Material Passes", fallback: false);
		RemoveLegacy(config, "06 - Rendering", "Suppress Duplicate Material Passes", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Enable CustomLavaDamagePatch", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Enable PlainsMiniHeatPatches", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Enable MonsterDoorSensor", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Enable MonsterClimbPatch", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Enable Smooth Stamina Regen Patch", fallback: false);
		RemoveLegacy(config, "Balrond Hot Paths", "Replace Deep North Reflection", fallback: true);
		RemoveLegacy(config, "Balrond Hot Paths", "Optimize Fermenter Awake", fallback: true);
		RemoveLegacy(config, "Balrond Hot Paths", "Optimize Dverger Aggro Hook", fallback: true);
		RemoveLegacy(config, "Balrond Bug Fixes", "Fix Biome Message Width", fallback: true);
		RemoveLegacy(config, "Balrond Status Effects", "Throttle Food Status Hook", fallback: true);
		RemoveLegacy(config, "Balrond Status Effects", "Throttle Environment Status Hooks", fallback: true);
		RemoveLegacy(config, "Balrond Status Effects", "Environment Status Interval", 0.25f);
		RemoveLegacy(config, "Balrond Streaming", "Reduce Iced Tree Wind Update Rate", fallback: true);
		RemoveLegacy(config, "Balrond Streaming", "Disable Outdoor Stalagmite Material Poll", fallback: true);
		RemoveLegacy(config, "Balrond Streaming", "Skip Idle Converter Catch-up", fallback: true);
		RemoveLegacy(config, "Balrond Streaming", "Stagger Converter Updates", fallback: true);
		RemoveLegacy(config, "Balrond GPU", "Enable Heavy Tree LODs", fallback: true);
		RemoveLegacy(config, "Balrond GPU", "Heavy Tree Far LOD Screen Height", 0.2f);
		RemoveLegacy(config, "Balrond GPU", "Heavy Tree Cull Screen Height", 0.02f);
		RemoveLegacy(config, "Balrond GPU", "Enable Mip-Based Large Texture Reduction", fallback: true);
		RemoveLegacy(config, "Balrond GPU", "Large Texture Minimum Size", 1024);
		RemoveLegacy(config, "Balrond GPU", "Large Texture Mip Offset", 1);
		RemoveLegacy(config, "Balrond Lifecycle", "Fix Recipe Accumulation", fallback: true);
		RemoveLegacy(config, "Balrond Lifecycle", "Fix Tutorial Accumulation", fallback: true);
		RemoveLegacy(config, "Balrond Lifecycle", "Fix Tentacle Prefab Lifecycle", fallback: true);
		return result;
	}

	private static HashSet<ConfigDefinition> CaptureExistingConfigDefinitions(ConfigFile config)
	{
		HashSet<ConfigDefinition> hashSet = new HashSet<ConfigDefinition>(config.Keys);
		if (typeof(ConfigFile).GetProperty("OrphanedEntries", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(config) is IDictionary dictionary)
		{
			foreach (DictionaryEntry item in dictionary)
			{
				object key = item.Key;
				ConfigDefinition val = (ConfigDefinition)((key is ConfigDefinition) ? key : null);
				if (val != null)
				{
					hashSet.Add(val);
				}
			}
		}
		else
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogWarning((object)"Could not inspect unbound BepInEx config entries; legacy values will not overwrite current 0.7.7 keys.");
			}
		}
		return hashSet;
	}

	private static void ApplyLegacyValue<T>(ConfigEntry<T> current, T legacyValue, LegacyValues legacy, string legacySection, string legacyKey)
	{
		//IL_0003: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Expected O, but got Unknown
		ConfigDefinition item = new ConfigDefinition(legacySection, legacyKey);
		if (legacy.ExistingDefinitions.Contains(item) && !legacy.ExistingDefinitions.Contains(((ConfigEntryBase)current).Definition))
		{
			current.Value = legacyValue;
		}
	}

	private static T TakeLegacy<T>(ConfigFile config, string section, string key, T fallback)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Expected O, but got Unknown
		ConfigDefinition val = new ConfigDefinition(section, key);
		try
		{
			T value = config.Bind<T>(val, fallback, (ConfigDescription)null).Value;
			config.Remove(val);
			return value;
		}
		catch (Exception ex)
		{
			config.Remove(val);
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogDebug((object)("Could not migrate legacy config " + section + "/" + key + ": " + ex.Message));
			}
			return fallback;
		}
	}

	private static void RemoveLegacy<T>(ConfigFile config, string section, string key, T fallback)
	{
		TakeLegacy(config, section, key, fallback);
	}

	private static string RestartRequired(string description)
	{
		return "[Restart required] " + description;
	}

	private static void OnConfigSettingChanged(object sender, SettingChangedEventArgs args)
	{
		if (_settingsCaptured && !_restartWarningLogged)
		{
			_restartWarningLogged = true;
			object obj;
			if (args == null)
			{
				obj = null;
			}
			else
			{
				ConfigEntryBase changedSetting = args.ChangedSetting;
				obj = ((changedSetting == null) ? null : ((object)changedSetting.Definition)?.ToString());
			}
			if (obj == null)
			{
				obj = "optimizer setting";
			}
			Warn((string?)obj + " was saved, but runtime patches were not changed. Restart Valheim to apply optimizer settings.");
		}
	}

	private static RuntimeOptions CaptureOptions()
	{
		return new RuntimeOptions
		{
			Enabled = OptimizerEnabled.Value,
			DisableMonsterClimbing = DisableMonsterClimbing.Value,
			ReplaceStableHashReflection = ReplaceStableHashReflection.Value,
			DisableServingTray = !EnableServingTrayIntegration.Value,
			DisableServingTrayAutoRegistration = DisableServingTrayAutoRegistration.Value,
			ExcludeHeavyFoodDisplays = ExcludeHeavyFoodDisplays.Value,
			DisablePoisonGeysers = DisablePoisonGeysers.Value,
			OptimizeEnabledPoisonGeysers = OptimizeEnabledPoisonGeysers.Value,
			GeyserParticleLimit = GeyserParticleLimit.Value,
			GeyserMaxNear = GeyserMaxNear.Value,
			GeyserMaxTotal = GeyserMaxTotal.Value,
			DisableSwampClutter = DisableSwampClutter.Value,
			DisableSwampVisualVegetation = DisableSwampVisualVegetation.Value,
			DisableSwampResourceVegetation = DisableSwampResourceVegetation.Value,
			BalrondClutterDensityPercent = BalrondClutterDensityPercent.Value,
			DisableNonInstancedBalrondClutter = DisableNonInstancedBalrondClutter.Value,
			OptimizePortalEffects = OptimizePortalEffects.Value,
			PortalParticleLimit = PortalParticleLimit.Value,
			OptimizeFloatingDebrisEffects = OptimizeFloatingDebrisEffects.Value,
			FloatingDebrisParticleLimit = FloatingDebrisParticleLimit.Value,
			ConservativeMaterialStateFix = (ConservativeMaterialStateFix.Value || AggressiveOpaqueMaterialRestore.Value),
			AggressiveOpaqueMaterialRestore = AggressiveOpaqueMaterialRestore.Value,
			DisableKnownHeavyPrefabShadows = DisableKnownHeavyPrefabShadows.Value,
			FixBalrondConstructionsNullHitEffects = FixBalrondConstructionsNullHitEffects.Value,
			SmoothDistantTerrainRebuild = SmoothDistantTerrainRebuild.Value,
			DistantTerrainRegionsPerFrame = DistantTerrainRegionsPerFrame.Value,
			CacheVanillaZdoSortComparers = CacheVanillaZdoSortComparers.Value,
			StaticPhysicsRecheckInterval = StaticPhysicsRecheckInterval.Value,
			TimeSliceTerrainClutterRebuilds = TimeSliceTerrainClutterRebuilds.Value
		};
	}

	internal static void Apply()
	{
		Options = CaptureOptions();
		_settingsCaptured = true;
		if (!Options.Enabled)
		{
			Log.LogInfo((object)"BalrondNatureOptimizer is disabled by its startup master switch. No patches were installed.");
			return;
		}
		if (!ValidateCompatibilityContract(out var capabilityCount, out var failure))
		{
			Log.LogError((object)("Amazing Nature compatibility contract rejected: " + failure + " No optimizer patches were installed."));
			return;
		}
		StartupIssues.Clear();
		_collectStartupIssues = true;
		TryInstall("Monster climbing", InstallMonsterClimbingControl);
		TryInstall("StableHash", InstallStableHashFix);
		TryInstall("Material state", InstallMaterialStateFixes);
		TryInstall("Serving Tray", InstallServingTrayOptimizations);
		TryInstall("Vanilla streaming", InstallStreamingOptimizations);
		TryInstall("World templates", InstallWorldOptimizations);
		TryInstall("Session cleanup", InstallSessionCleanup);
		_collectStartupIssues = false;
		Log.LogInfo((object)("BalrondNatureOptimizer 0.7.7 / " + string.Format("Amazing Nature {0}: capabilities={1}/{2}, startupIssues={3}.", "1.3.8", capabilityCount, capabilityCount, StartupIssues.Count)));
		if (StartupIssues.Count > 0)
		{
			Log.LogWarning((object)("Optimizer startup issues: " + string.Join(" | ", StartupIssues)));
		}
	}

	internal static void Shutdown()
	{
		Patcher.UnpatchSelf();
		ResetPluginBindings();
		if (_config != null)
		{
			_config.SettingChanged -= OnConfigSettingChanged;
			_config = null;
		}
		Options = null;
		_balrondAssembly = null;
		TypeCache.Clear();
		FieldCache.Clear();
		StartupIssues.Clear();
		_settingsCaptured = false;
		_restartWarningLogged = false;
		_collectStartupIssues = false;
		_initialized = false;
	}

	private static bool ValidateCompatibilityContract(out int capabilityCount, out string failure)
	{
		capabilityCount = 0;
		failure = null;
		if (!Chainloader.PluginInfos.TryGetValue("balrond.astafaraios.BalrondAmazingNature", out var value))
		{
			failure = "plugin GUID balrond.astafaraios.BalrondAmazingNature is not loaded.";
			return false;
		}
		Version version = value.Metadata.Version;
		if (version == null || version != SupportedBalrondVersionValue)
		{
			failure = "expected exactly 1.3.8, found " + (version?.ToString() ?? "unknown") + ".";
			return false;
		}
		_balrondAssembly = ((object)value.Instance)?.GetType().Assembly;
		if (_balrondAssembly == null)
		{
			failure = "the loaded plugin assembly could not be resolved.";
			return false;
		}
		List<string> list = new List<string>();
		if (Options.ReplaceStableHashReflection)
		{
			RequireMethod(list, "BalrondHashCompat", "StableHash", new Type[1] { typeof(string) }, typeof(int), isStatic: true);
		}
		if (Options.DisableMonsterClimbing)
		{
			RequireMethod(list, "BalrondNature.CharacterRuntimePatches+Character_CustomFixedUpdate_Patch", "Postfix", new Type[2]
			{
				typeof(Character),
				typeof(float)
			}, typeof(void), isStatic: true);
			RequireMethod(list, "BalrondNature.CharacterRuntimePatches+Character_OnDestroy_Patch", "Prefix", new Type[1] { typeof(Character) }, typeof(void), isStatic: true);
			RequireMonsterClimbingStatesField(list);
			RequireMethod(list, typeof(Character), "CustomFixedUpdate", new Type[1] { typeof(float) }, typeof(void), isStatic: false);
			RequireMethod(list, typeof(Character), "OnDestroy", Type.EmptyTypes, typeof(void), isStatic: false);
		}
		if (Options.ConservativeMaterialStateFix)
		{
			RequireMethod(list, "BalrondNature.ShaderReplacement", "ReplaceShader", new Type[2]
			{
				typeof(Material),
				typeof(string)
			}, typeof(bool), isStatic: true);
			RequireMethod(list, "BalrondNature.ShaderReplacement", "ForceAlphaCutoutFix", new Type[1] { typeof(Material) }, typeof(void), isStatic: true);
		}
		if (Options.DisableServingTray)
		{
			RequireMethod(list, "BalrondNature.ServingTrayBuilder", "SetupBuildPieces", new Type[1] { typeof(List<GameObject>) }, typeof(void), isStatic: false);
		}
		else
		{
			if (Options.DisableServingTrayAutoRegistration)
			{
				RequireMethod(list, "BalrondNature.ServingTrayBuilder", "BuildAutoScanList", Type.EmptyTypes, typeof(List<GameObject>), isStatic: false);
			}
			if (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays)
			{
				RequireMethod(list, "BalrondNature.ServingTrayBuilder", "TryPrepareAsServingTrayPiece", new Type[7]
				{
					typeof(GameObject),
					typeof(Piece),
					typeof(WearNTear),
					typeof(Transform),
					typeof(PieceCategory),
					typeof(bool),
					typeof(string).MakeByRefType()
				}, typeof(bool), isStatic: true);
			}
		}
		if (Options.DisablePoisonGeysers)
		{
			RequireMethod(list, "BalrondNature.MonsterManager", "doSpawnerChanges", new Type[1] { typeof(string) }, typeof(void), isStatic: false);
		}
		if (Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation)
		{
			RequireField(list, "BalrondNature.VegetationBuilder", "ZoneVegetations", isStatic: true, isReadOnly: false, typeof(List<ZoneVegetation>));
			RequireMethod(list, typeof(ZoneSystem), "SetupLocations", Type.EmptyTypes, typeof(void), isStatic: false);
		}
		if (Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter)
		{
			RequireField(list, "BalrondNature.Launch", "clutterBuilder", isStatic: true, isReadOnly: false, null, "BalrondNature.ClutterBuilder");
			RequireField(list, "BalrondNature.ClutterBuilder", "clutterObjects", isStatic: false, isReadOnly: true, typeof(List<Clutter>));
			RequireMethod(list, typeof(ClutterSystem), "Awake", Type.EmptyTypes, typeof(void), isStatic: false);
		}
		if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects)
		{
			RequireMethod(list, typeof(ZNetScene), "Awake", Type.EmptyTypes, typeof(void), isStatic: false);
		}
		RequireMethod(list, typeof(Game), "Shutdown", new Type[1] { typeof(bool) }, typeof(void), isStatic: false);
		if (Options.SmoothDistantTerrainRebuild)
		{
			RequireMethod(list, typeof(TerrainLod), "RebuildAllHeightmaps", Type.EmptyTypes, typeof(void), isStatic: false);
			RequireMethod(list, typeof(TerrainLod), "OnEnable", Type.EmptyTypes, typeof(void), isStatic: false);
			RequireMethod(list, typeof(TerrainLod), "OnDisable", Type.EmptyTypes, typeof(void), isStatic: false);
		}
		if (Options.CacheVanillaZdoSortComparers)
		{
			Type nestedType = typeof(ZDOMan).GetNestedType("ZDOPeer", BindingFlags.NonPublic);
			Type[] arguments = new Type[2]
			{
				typeof(ZDO),
				typeof(ZDO)
			};
			RequireMethod(list, typeof(ZNetScene), "CreateObjectsSorted", new Type[3]
			{
				typeof(List<ZDO>),
				typeof(int),
				typeof(int).MakeByRefType()
			}, typeof(void), isStatic: false);
			RequireMethod(list, typeof(ZNetScene), "ZDOCompare", arguments, typeof(int), isStatic: true);
			if (nestedType == null)
			{
				list.Add("ZDOMan.ZDOPeer");
			}
			else
			{
				RequireMethod(list, typeof(ZDOMan), "ServerSortSendZDOS", new Type[3]
				{
					typeof(List<ZDO>),
					typeof(Vector3),
					nestedType
				}, typeof(void), isStatic: false);
				RequireMethod(list, typeof(ZDOMan), "ClientSortSendZDOS", new Type[2]
				{
					typeof(List<ZDO>),
					nestedType
				}, typeof(void), isStatic: false);
			}
			RequireMethod(list, typeof(ZDOMan), "ServerSendCompare", arguments, typeof(int), isStatic: true);
			RequireMethod(list, typeof(ZDOMan), "ClientSendCompare", arguments, typeof(int), isStatic: true);
		}
		if (Options.StaticPhysicsRecheckInterval > 0f)
		{
			RequireMethod(list, typeof(StaticPhysics), "SUpdate", new Type[2]
			{
				typeof(float),
				typeof(Vector2i)
			}, typeof(void), isStatic: false);
		}
		if (Options.TimeSliceTerrainClutterRebuilds)
		{
			RequireMethod(list, typeof(ClutterSystem), "ResetGrass", new Type[2]
			{
				typeof(Vector3),
				typeof(float)
			}, typeof(void), isStatic: false);
		}
		capabilityCount = CountRequestedCapabilities();
		if (list.Count == 0)
		{
			return true;
		}
		failure = string.Format("{0} required member(s) are missing or incompatible: {1}.", list.Count, string.Join(", ", list));
		return false;
	}

	private static int CountRequestedCapabilities()
	{
		return 1 + (Options.ReplaceStableHashReflection ? 1 : 0) + (Options.DisableMonsterClimbing ? 5 : 0) + (Options.ConservativeMaterialStateFix ? 2 : 0) + (Options.DisableServingTray ? 1 : 0) + ((!Options.DisableServingTray && Options.DisableServingTrayAutoRegistration) ? 1 : 0) + ((!Options.DisableServingTray && (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays)) ? 1 : 0) + (Options.DisablePoisonGeysers ? 1 : 0) + ((Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation) ? 2 : 0) + ((Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter) ? 3 : 0) + ((Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects) ? 1 : 0) + (Options.SmoothDistantTerrainRebuild ? 3 : 0) + (Options.CacheVanillaZdoSortComparers ? 6 : 0) + ((Options.StaticPhysicsRecheckInterval > 0f) ? 1 : 0) + (Options.TimeSliceTerrainClutterRebuilds ? 1 : 0);
	}

	private static void RequireMethod(List<string> missing, string typeName, string methodName, Type[] arguments, Type returnType, bool isStatic)
	{
		MethodInfo methodInfo = FindMethod(FindType(typeName), methodName, arguments);
		if (methodInfo == null || methodInfo.IsStatic != isStatic || (returnType != null && methodInfo.ReturnType != returnType))
		{
			missing.Add(typeName + "." + methodName);
		}
	}

	private static void RequireMethod(List<string> missing, Type type, string methodName, Type[] arguments, Type returnType, bool isStatic)
	{
		MethodInfo methodInfo = FindMethod(type, methodName, arguments);
		if (methodInfo == null || methodInfo.IsStatic != isStatic || methodInfo.ReturnType != returnType)
		{
			missing.Add(type.FullName + "." + methodName);
		}
	}

	private static void RequireField(List<string> missing, string typeName, string fieldName, bool isStatic, bool isReadOnly, Type expectedType = null, string expectedTypeName = null)
	{
		FieldInfo fieldInfo = FindField(FindType(typeName), fieldName);
		if (fieldInfo == null || fieldInfo.IsStatic != isStatic || fieldInfo.IsInitOnly != isReadOnly || (expectedType != null && fieldInfo.FieldType != expectedType) || (expectedTypeName != null && fieldInfo.FieldType.FullName != expectedTypeName))
		{
			missing.Add(typeName + "." + fieldName);
		}
	}

	private static void RequireMonsterClimbingStatesField(List<string> missing)
	{
		FieldInfo fieldInfo = FindField(FindType("BalrondNature.CharacterRuntimePatches+MonsterClimbing"), "States");
		Type type = fieldInfo?.FieldType;
		Type[] array = (((object)type != null && type.IsGenericType) ? type.GetGenericArguments() : Type.EmptyTypes);
		bool flag = (object)type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<, >) && array.Length == 2 && array[0] == typeof(Character) && array[1].FullName == "BalrondNature.CharacterRuntimePatches+MonsterClimbing+ClimbState";
		if (fieldInfo == null || !fieldInfo.IsStatic || !fieldInfo.IsInitOnly || !flag)
		{
			missing.Add("BalrondNature.CharacterRuntimePatches+MonsterClimbing.States");
		}
	}

	private static void TryInstall(string feature, Action install)
	{
		try
		{
			install();
		}
		catch (Exception ex)
		{
			Warn(feature + ": " + ex.GetType().Name + ": " + ex.Message);
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogDebug((object)ex);
			}
		}
	}

	private static void InstallMonsterClimbingControl()
	{
		if (!Options.DisableMonsterClimbing)
		{
			return;
		}
		MethodInfo original = FindMethod(typeof(Character), "CustomFixedUpdate", new Type[1] { typeof(float) });
		MethodInfo patch = FindMethod(FindType("BalrondNature.CharacterRuntimePatches+Character_CustomFixedUpdate_Patch"), "Postfix", new Type[2]
		{
			typeof(Character),
			typeof(float)
		});
		if (UnpatchExact(original, patch, "Monster Climbing CustomFixedUpdate postfix"))
		{
			object obj = FindField(FindType("BalrondNature.CharacterRuntimePatches+MonsterClimbing"), "States")?.GetValue(null);
			if (!(obj is IDictionary))
			{
				Warn("Monster Climbing update was disabled, but its States dictionary could not be cleared; OnDestroy cleanup remains installed.");
				return;
			}
			int num = ClearCollection(obj);
			MethodInfo original2 = FindMethod(typeof(Character), "OnDestroy", Type.EmptyTypes);
			MethodInfo patch2 = FindMethod(FindType("BalrondNature.CharacterRuntimePatches+Character_OnDestroy_Patch"), "Prefix", new Type[1] { typeof(Character) });
			bool flag = UnpatchExact(original2, patch2, "Monster Climbing OnDestroy cleanup prefix");
			Log.LogInfo((object)$"Monster Climbing disabled; cleared {num} cached state(s), cleanupRemoved={flag}.");
		}
	}

	private static void InstallStableHashFix()
	{
		if (Options.ReplaceStableHashReflection)
		{
			PatchPrefix(FindType("BalrondHashCompat"), "StableHash", "BalrondStableHashPrefix", new Type[1] { typeof(string) });
		}
	}

	private static void InstallMaterialStateFixes()
	{
		if (Options.ConservativeMaterialStateFix)
		{
			Type originalType = FindType("BalrondNature.ShaderReplacement");
			Type[] arguments = new Type[2]
			{
				typeof(Material),
				typeof(string)
			};
			Type[] arguments2 = new Type[1] { typeof(Material) };
			if (!PatchPrefix(originalType, "ReplaceShader", "ReplaceShaderPrefix", arguments) || !PatchPostfix(originalType, "ReplaceShader", "ReplaceShaderPostfix", arguments) || !PatchPrefix(originalType, "ForceAlphaCutoutFix", "ForceAlphaCutoutFixPrefix", arguments2))
			{
				UnpatchOwn(originalType, "ReplaceShader", "ReplaceShaderPrefix", arguments);
				UnpatchOwn(originalType, "ReplaceShader", "ReplaceShaderPostfix", arguments);
				UnpatchOwn(originalType, "ForceAlphaCutoutFix", "ForceAlphaCutoutFixPrefix", arguments2);
			}
		}
	}

	private static void InstallServingTrayOptimizations()
	{
		if (!Options.DisableServingTray && !Options.DisableServingTrayAutoRegistration && !Options.ExcludeHeavyFoodDisplays)
		{
			return;
		}
		Type originalType = FindType("BalrondNature.ServingTrayBuilder");
		if (Options.DisableServingTray)
		{
			PatchPrefix(originalType, "SetupBuildPieces", "ServingTraySetupPrefix", new Type[1] { typeof(List<GameObject>) });
			return;
		}
		if (Options.DisableServingTrayAutoRegistration)
		{
			PatchPrefix(originalType, "BuildAutoScanList", "ServingTrayBuildAutoScanListPrefix", Type.EmptyTypes);
		}
		if (Options.DisableServingTrayAutoRegistration || Options.ExcludeHeavyFoodDisplays)
		{
			PatchPrefix(originalType, "TryPrepareAsServingTrayPiece", "ServingTrayPreparePrefix", new Type[7]
			{
				typeof(GameObject),
				typeof(Piece),
				typeof(WearNTear),
				typeof(Transform),
				typeof(PieceCategory),
				typeof(bool),
				typeof(string).MakeByRefType()
			});
		}
	}

	private static void InstallWorldOptimizations()
	{
		if (Options.DisablePoisonGeysers || Options.OptimizeEnabledPoisonGeysers || Options.OptimizePortalEffects || Options.OptimizeFloatingDebrisEffects || Options.DisableKnownHeavyPrefabShadows || Options.FixBalrondConstructionsNullHitEffects)
		{
			PatchPostfixAfterBalrond(typeof(ZNetScene), "Awake", "ZNetSceneAwakePostfix");
		}
		if (Options.DisablePoisonGeysers)
		{
			PatchPrefix(FindType("BalrondNature.MonsterManager"), "doSpawnerChanges", "MonsterSpawnerSetupPrefix");
		}
		if (Options.DisablePoisonGeysers || Options.DisableSwampVisualVegetation || Options.DisableSwampResourceVegetation)
		{
			PatchPrefixBeforeBalrond(typeof(ZoneSystem), "SetupLocations", "ZoneSystemSetupLocationsPrefix");
			PatchPostfixAfterBalrond(typeof(ZoneSystem), "SetupLocations", "ZoneSystemSetupLocationsPostfix");
		}
		if (Options.DisableSwampClutter || Options.BalrondClutterDensityPercent < 100 || Options.DisableNonInstancedBalrondClutter)
		{
			PatchPrefixBeforeBalrond(typeof(ClutterSystem), "Awake", "ClutterSystemAwakePrefix");
		}
	}

	private static void InstallStreamingOptimizations()
	{
		if (Options.CacheVanillaZdoSortComparers)
		{
			InstallZdoSortComparerCache();
		}
		if (Options.SmoothDistantTerrainRebuild)
		{
			InstallTerrainLodSmoothing();
		}
		if (Options.StaticPhysicsRecheckInterval > 0f)
		{
			InstallStaticPhysicsCadenceFix();
		}
		if (Options.TimeSliceTerrainClutterRebuilds)
		{
			InstallTerrainTriggeredClutterSmoothing();
		}
	}

	private static void InstallSessionCleanup()
	{
		PatchPostfix(typeof(Game), "Shutdown", "GameShutdownPostfix", new Type[1] { typeof(bool) });
	}

	internal static MethodInfo FindMethod(Type type, string name, Type[] arguments = null)
	{
		if (type == null)
		{
			return null;
		}
		if (arguments != null)
		{
			return AccessTools.Method(type, name, arguments, (Type[])null);
		}
		return AccessTools.Method(type, name, (Type[])null, (Type[])null);
	}

	internal static Type FindType(string typeName)
	{
		if (string.IsNullOrEmpty(typeName))
		{
			return null;
		}
		if (TypeCache.TryGetValue(typeName, out var value))
		{
			return value;
		}
		Type type = _balrondAssembly?.GetType(typeName, throwOnError: false, ignoreCase: false);
		TypeCache[typeName] = type;
		return type;
	}

	internal static FieldInfo FindField(Type type, string fieldName)
	{
		if (type == null || string.IsNullOrEmpty(fieldName))
		{
			return null;
		}
		if (!FieldCache.TryGetValue(type, out var value))
		{
			value = new Dictionary<string, FieldInfo>(StringComparer.Ordinal);
			FieldCache[type] = value;
		}
		if (value.TryGetValue(fieldName, out var value2))
		{
			return value2;
		}
		return value[fieldName] = AccessTools.Field(type, fieldName);
	}

	private static bool PatchPrefix(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		return Patch(originalType, originalName, patchName, prefix: true, arguments, null, null);
	}

	private static bool PatchPrefixBeforeBalrond(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		return Patch(originalType, originalName, patchName, prefix: true, arguments, new string[1] { "balrond.astafaraios.BalrondAmazingNature" }, null);
	}

	private static bool PatchPostfix(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		return Patch(originalType, originalName, patchName, prefix: false, arguments, null, null);
	}

	private static bool PatchPostfixAfterBalrond(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		return Patch(originalType, originalName, patchName, prefix: false, arguments, null, new string[1] { "balrond.astafaraios.BalrondAmazingNature" });
	}

	private static bool PatchTranspiler(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = FindMethod(originalType, originalName, arguments);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Warn("Transpiler target missing: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ".");
				return false;
			}
			Patcher.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null);
			return true;
		}
		catch (Exception ex)
		{
			Warn("Transpiler target incompatible: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ": " + ex.GetType().Name + ": " + ex.Message);
			return false;
		}
	}

	private static bool Patch(Type originalType, string originalName, string patchName, bool prefix, Type[] arguments, string[] before, string[] after)
	{
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Expected O, but got Unknown
		try
		{
			MethodInfo methodInfo = FindMethod(originalType, originalName, arguments);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Warn("Patch target missing: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ".");
				return false;
			}
			HarmonyMethod val = new HarmonyMethod(methodInfo2)
			{
				before = before,
				after = after
			};
			if (prefix)
			{
				Patcher.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				Patcher.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			return true;
		}
		catch (Exception ex)
		{
			Warn("Patch target incompatible: " + (originalType?.FullName ?? "<null>") + "." + originalName + " <- " + patchName + ": " + ex.GetType().Name + ": " + ex.Message);
			return false;
		}
	}

	private static bool UnpatchExact(MethodInfo original, MethodInfo patch, string label)
	{
		if (original == null || patch == null)
		{
			Warn(label + " could not be resolved.");
			return false;
		}
		try
		{
			if (!IsHarmonyPatchInstalled(original, patch))
			{
				Warn(label + " was not installed; no upstream patch was removed.");
				return false;
			}
			Patcher.Unpatch((MethodBase)original, patch);
			if (IsHarmonyPatchInstalled(original, patch))
			{
				Warn(label + " remained installed after the exact unpatch request.");
				return false;
			}
			return true;
		}
		catch (Exception ex)
		{
			Warn(label + " could not be removed: " + ex.GetType().Name + ": " + ex.Message);
			return false;
		}
	}

	private static void UnpatchOwn(Type originalType, string originalName, string patchName, Type[] arguments = null)
	{
		MethodInfo methodInfo = FindMethod(originalType, originalName, arguments);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(OptimizerRuntime), patchName, (Type[])null, (Type[])null);
		if (methodInfo != null && methodInfo2 != null)
		{
			Patcher.Unpatch((MethodBase)methodInfo, methodInfo2);
		}
	}

	private static bool IsHarmonyPatchInstalled(MethodInfo original, MethodInfo patchMethod)
	{
		Patches patchInfo = Harmony.GetPatchInfo((MethodBase)original);
		if (patchInfo == null)
		{
			return false;
		}
		foreach (Patch prefix in patchInfo.Prefixes)
		{
			if (prefix.PatchMethod == patchMethod)
			{
				return true;
			}
		}
		foreach (Patch postfix in patchInfo.Postfixes)
		{
			if (postfix.PatchMethod == patchMethod)
			{
				return true;
			}
		}
		foreach (Patch transpiler in patchInfo.Transpilers)
		{
			if (transpiler.PatchMethod == patchMethod)
			{
				return true;
			}
		}
		foreach (Patch finalizer in patchInfo.Finalizers)
		{
			if (finalizer.PatchMethod == patchMethod)
			{
				return true;
			}
		}
		return false;
	}

	private static bool TryCreateDelegate<T>(MethodInfo method, out T result) where T : class
	{
		result = null;
		if (method == null)
		{
			return false;
		}
		try
		{
			result = Delegate.CreateDelegate(typeof(T), method) as T;
			return result != null;
		}
		catch
		{
			return false;
		}
	}

	internal static object GetStaticField(string typeName, string fieldName)
	{
		return FindField(FindType(typeName), fieldName)?.GetValue(null);
	}

	internal static object GetInstanceField(object instance, string fieldName)
	{
		if (instance != null)
		{
			return FindField(instance.GetType(), fieldName)?.GetValue(instance);
		}
		return null;
	}

	internal static int ClearCollection(object collection)
	{
		if (collection == null)
		{
			return 0;
		}
		if (collection is IDictionary { Count: var count } dictionary)
		{
			dictionary.Clear();
			return count;
		}
		if (collection is IList { Count: var count2 } list)
		{
			list.Clear();
			return count2;
		}
		int result = ((collection is ICollection collection2) ? collection2.Count : 0);
		MethodInfo methodInfo = AccessTools.Method(collection.GetType(), "Clear", (Type[])null, (Type[])null);
		if ((object)methodInfo != null)
		{
			methodInfo.Invoke(collection, null);
			return result;
		}
		return result;
	}

	internal static void Warn(string message)
	{
		if (_collectStartupIssues)
		{
			if (!StartupIssues.Contains(message))
			{
				StartupIssues.Add(message);
			}
			return;
		}
		ManualLogSource log = Log;
		if (log != null)
		{
			log.LogWarning((object)message);
		}
	}

	private static void InstallZdoSortComparerCache()
	{
		InstallZdoSortComparerCache(typeof(ZNetScene), "CreateObjectsSorted", "ZDOCompare", ref _zNetSceneZdoComparison);
		InstallZdoSortComparerCache(typeof(ZDOMan), "ServerSortSendZDOS", "ServerSendCompare", ref _serverSendZdoComparison);
		InstallZdoSortComparerCache(typeof(ZDOMan), "ClientSortSendZDOS", "ClientSendCompare", ref _clientSendZdoComparison);
	}

	private static void InstallZdoSortComparerCache(Type owner, string sortMethod, string compareMethod, ref Comparison<ZDO> cache)
	{
		if (!TryCreateDelegate<Comparison<ZDO>>(FindMethod(owner, compareMethod, new Type[2]
		{
			typeof(ZDO),
			typeof(ZDO)
		}), out cache))
		{
			Warn("Cached ZDO comparer could not be bound for " + owner.FullName + "." + sortMethod + "; vanilla code was left unchanged.");
		}
		else if (!PatchTranspiler(owner, sortMethod, "CacheZdoComparisonTranspiler"))
		{
			cache = null;
		}
	}

	private static IEnumerable<CodeInstruction> CacheZdoComparisonTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
	{
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Expected O, but got Unknown
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		FieldInfo zdoComparisonCacheField = GetZdoComparisonCacheField(__originalMethod);
		MethodInfo expectedZdoComparison = GetExpectedZdoComparison(__originalMethod);
		ConstructorInfo objB = AccessTools.Constructor(typeof(Comparison<ZDO>), new Type[2]
		{
			typeof(object),
			typeof(IntPtr)
		}, false);
		int num = -1;
		int num2 = 0;
		for (int i = 0; i <= list.Count - 3; i++)
		{
			if (!(list[i].opcode != OpCodes.Ldnull) && !(list[i + 1].opcode != OpCodes.Ldftn) && object.Equals(list[i + 1].operand as MethodInfo, expectedZdoComparison) && !(list[i + 2].opcode != OpCodes.Newobj) && object.Equals(list[i + 2].operand as ConstructorInfo, objB))
			{
				num = i;
				num2++;
			}
		}
		if (zdoComparisonCacheField == null || expectedZdoComparison == null || num2 != 1)
		{
			Warn("Cached ZDO comparer IL pattern mismatch in " + __originalMethod?.DeclaringType?.FullName + "." + __originalMethod?.Name + "; vanilla allocation was left intact.");
			return list;
		}
		CodeInstruction val = new CodeInstruction(OpCodes.Ldsfld, (object)zdoComparisonCacheField);
		MoveInstructionMetadata(list[num], val);
		MoveInstructionMetadata(list[num + 1], val);
		MoveInstructionMetadata(list[num + 2], val);
		list.RemoveRange(num, 3);
		list.Insert(num, val);
		Log.LogInfo((object)("Cached the exact ZDO sort comparer used by " + __originalMethod.DeclaringType?.Name + "." + __originalMethod.Name + "."));
		return list;
	}

	private static FieldInfo GetZdoComparisonCacheField(MethodBase original)
	{
		if (original?.DeclaringType == typeof(ZNetScene) && original.Name == "CreateObjectsSorted")
		{
			return AccessTools.Field(typeof(OptimizerRuntime), "_zNetSceneZdoComparison");
		}
		if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ServerSortSendZDOS")
		{
			return AccessTools.Field(typeof(OptimizerRuntime), "_serverSendZdoComparison");
		}
		if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ClientSortSendZDOS")
		{
			return AccessTools.Field(typeof(OptimizerRuntime), "_clientSendZdoComparison");
		}
		return null;
	}

	private static MethodInfo GetExpectedZdoComparison(MethodBase original)
	{
		if (original?.DeclaringType == typeof(ZNetScene) && original.Name == "CreateObjectsSorted")
		{
			return FindMethod(typeof(ZNetScene), "ZDOCompare", new Type[2]
			{
				typeof(ZDO),
				typeof(ZDO)
			});
		}
		if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ServerSortSendZDOS")
		{
			return FindMethod(typeof(ZDOMan), "ServerSendCompare", new Type[2]
			{
				typeof(ZDO),
				typeof(ZDO)
			});
		}
		if (original?.DeclaringType == typeof(ZDOMan) && original.Name == "ClientSortSendZDOS")
		{
			return FindMethod(typeof(ZDOMan), "ClientSendCompare", new Type[2]
			{
				typeof(ZDO),
				typeof(ZDO)
			});
		}
		return null;
	}

	private static void InstallStaticPhysicsCadenceFix()
	{
		_staticPhysicsRecheckInterval = Options.StaticPhysicsRecheckInterval;
		if (!PatchTranspiler(typeof(StaticPhysics), "SUpdate", "StaticPhysicsCadenceTranspiler", new Type[2]
		{
			typeof(float),
			typeof(Vector2i)
		}))
		{
			_staticPhysicsRecheckInterval = 0f;
		}
	}

	private static void InstallTerrainLodSmoothing()
	{
		Type nestedType = typeof(TerrainLod).GetNestedType("HeightmapWithOffset", BindingFlags.NonPublic);
		Type nestedType2 = typeof(TerrainLod).GetNestedType("HeightmapState", BindingFlags.NonPublic);
		_terrainLodHeightmapsField = AccessTools.Field(typeof(TerrainLod), "m_heightmaps");
		_terrainLodTopStateField = AccessTools.Field(typeof(TerrainLod), "m_heightmapState");
		_terrainEntryHeightmapField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_heightmap"));
		_terrainEntryOffsetField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_offset"));
		_terrainEntryStateField = ((nestedType == null) ? null : AccessTools.Field(nestedType, "m_state"));
		if (nestedType == null || nestedType2 == null || _terrainLodHeightmapsField == null || _terrainLodTopStateField == null || _terrainEntryHeightmapField == null || _terrainEntryOffsetField == null || _terrainEntryStateField == null)
		{
			Warn("TerrainLod private layout did not match the audited game build; distant-terrain smoothing was not installed.");
			ResetTerrainLodSmoothing();
			return;
		}
		try
		{
			if (Enum.GetUnderlyingType(nestedType2) != typeof(int))
			{
				throw new InvalidOperationException("TerrainLod.HeightmapState no longer uses Int32 storage.");
			}
			_terrainReadyState = Convert.ToInt32(Enum.Parse(nestedType2, "ReadyToRebuild"));
			_terrainDoneState = Convert.ToInt32(Enum.Parse(nestedType2, "Done"));
			_terrainEntryStateSetter = CreatePrivateEnumSetter<object>(nestedType, _terrainEntryStateField);
			_terrainTopStateSetter = CreatePrivateEnumSetter<TerrainLod>(typeof(TerrainLod), _terrainLodTopStateField);
			_heightmapBuildDataRef = AccessTools.FieldRefAccess<Heightmap, HMBuildData>("m_buildData");
			_heightmapCornerBiomesRef = AccessTools.FieldRefAccess<Heightmap, Biome[]>("m_cornerBiomes");
		}
		catch (Exception ex)
		{
			Warn("TerrainLod field bindings could not be created; distant-terrain smoothing was not installed: " + ex.Message);
			ResetTerrainLodSmoothing();
			return;
		}
		if (HasForeignTerrainLodPatch(FindMethod(typeof(TerrainLod), "RebuildAllHeightmaps")))
		{
			Warn("TerrainLod.RebuildAllHeightmaps already has a Harmony patch; distant-terrain smoothing was skipped to avoid a competing streamer.");
			ResetTerrainLodSmoothing();
			return;
		}
		bool flag = PatchPrefix(typeof(TerrainLod), "RebuildAllHeightmaps", "TerrainLodRebuildPrefix");
		bool flag2 = PatchPostfix(typeof(TerrainLod), "OnEnable", "TerrainLodOnEnablePostfix");
		bool flag3 = PatchPostfix(typeof(TerrainLod), "OnDisable", "TerrainLodOnDisablePostfix");
		if (!(flag && flag2 && flag3))
		{
			if (flag)
			{
				UnpatchOwn(typeof(TerrainLod), "RebuildAllHeightmaps", "TerrainLodRebuildPrefix");
			}
			if (flag2)
			{
				UnpatchOwn(typeof(TerrainLod), "OnEnable", "TerrainLodOnEnablePostfix");
			}
			if (flag3)
			{
				UnpatchOwn(typeof(TerrainLod), "OnDisable", "TerrainLodOnDisablePostfix");
			}
			Warn("TerrainLod smoothing was rolled back because its lifecycle targets were not all available.");
			ResetTerrainLodSmoothing();
		}
	}

	private static void TerrainLodOnEnablePostfix(TerrainLod __instance)
	{
		if (!_terrainLodCompatibilityChecked)
		{
			_terrainLodCompatibilityChecked = true;
			if (HasForeignTerrainLodPatch(FindMethod(typeof(TerrainLod), "RebuildAllHeightmaps", Type.EmptyTypes)))
			{
				_terrainLodSmoothingFaulted = true;
				TerrainLodCycles.Clear();
				Warn("A later-loaded Harmony patch also targets TerrainLod.RebuildAllHeightmaps; distant-terrain smoothing failed open to vanilla for this session.");
			}
		}
		if ((Object)(object)__instance != (Object)null && !_terrainLodSmoothingFaulted)
		{
			PrepareTerrainLodCycle(__instance);
		}
	}

	private static bool HasForeignTerrainLodPatch(MethodInfo rebuild)
	{
		Patches val = ((rebuild == null) ? null : Harmony.GetPatchInfo((MethodBase)rebuild));
		if (val != null)
		{
			if (!HasForeignPatchOwner(val.Prefixes) && !HasForeignPatchOwner(val.Postfixes) && !HasForeignPatchOwner(val.Transpilers))
			{
				return HasForeignPatchOwner(val.Finalizers);
			}
			return true;
		}
		return false;
	}

	private static bool HasForeignPatchOwner(IEnumerable<Patch> patches)
	{
		foreach (Patch patch in patches)
		{
			if (!string.Equals(patch.owner, "dragonmotion.balrondnatureoptimizer", StringComparison.Ordinal))
			{
				return true;
			}
		}
		return false;
	}

	private static void TerrainLodOnDisablePostfix(TerrainLod __instance)
	{
		if ((Object)(object)__instance != (Object)null)
		{
			TerrainLodCycles.Remove(((Object)__instance).GetInstanceID());
		}
	}

	private static bool TerrainLodRebuildPrefix(TerrainLod __instance, Vector3 ___m_lastPoint)
	{
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null)
		{
			return true;
		}
		if (_terrainLodSmoothingFaulted)
		{
			TryRestoreTerrainCycleForVanilla(__instance);
			return true;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting())
		{
			TryRestoreTerrainCycleForVanilla(__instance);
			return true;
		}
		try
		{
			int instanceID = ((Object)__instance).GetInstanceID();
			if (!TerrainLodCycles.TryGetValue(instanceID, out var value) && !PrepareTerrainLodCycle(__instance))
			{
				return true;
			}
			value = TerrainLodCycles[instanceID];
			if (value.Count <= Options.DistantTerrainRegionsPerFrame)
			{
				return true;
			}
			if (Time.frameCount != _terrainBudgetFrame)
			{
				_terrainBudgetFrame = Time.frameCount;
				_terrainRegionsProcessedThisFrame = 0;
			}
			int num = Options.DistantTerrainRegionsPerFrame - _terrainRegionsProcessedThisFrame;
			if (num <= 0)
			{
				return false;
			}
			if (!value.Active)
			{
				if (!BeginTerrainLodCycle(value, ___m_lastPoint))
				{
					return true;
				}
			}
			else if (value.LastPoint != ___m_lastPoint || value.World != WorldGenerator.instance || value.Source.Count != value.Count)
			{
				throw new InvalidOperationException("TerrainLod changed while a sliced rebuild was active.");
			}
			int num2 = value.Count - value.Next;
			int num3 = Mathf.Min(num, num2);
			for (int i = 0; i < num3; i++)
			{
				int num4 = value.Next++;
				Heightmap val = value.Heightmaps[num4];
				HMBuildData val2 = value.Data[num4];
				if ((Object)(object)val == (Object)null || val2 == null)
				{
					throw new InvalidOperationException($"TerrainLod region {num4} lost its Heightmap/build data.");
				}
				_heightmapBuildDataRef.Invoke(val) = val2;
				_heightmapCornerBiomesRef.Invoke(val) = val2.m_cornerBiomes;
				((Component)val).transform.position = value.LastPoint + value.Offsets[num4];
				val.Regenerate();
				_terrainRegionsProcessedThisFrame++;
			}
			if (value.Next >= value.Count)
			{
				CompleteTerrainLodCycle(__instance, value);
			}
			return false;
		}
		catch (Exception arg)
		{
			_terrainLodSmoothingFaulted = true;
			Warn($"Distant-terrain smoothing failed open and is disabled for this session: {arg}");
			TryRestoreAllTerrainCyclesForVanilla();
			return true;
		}
	}

	private static bool PrepareTerrainLodCycle(TerrainLod terrainLod)
	{
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		if (!(_terrainLodHeightmapsField?.GetValue(terrainLod) is IList { Count: not 0 } list))
		{
			return false;
		}
		int instanceID = ((Object)terrainLod).GetInstanceID();
		if (!TerrainLodCycles.TryGetValue(instanceID, out var value) || value.Entries.Length != list.Count)
		{
			value = new TerrainLodCycle
			{
				Entries = new object[list.Count],
				Heightmaps = (Heightmap[])(object)new Heightmap[list.Count],
				Offsets = (Vector3[])(object)new Vector3[list.Count],
				Data = (HMBuildData[])(object)new HMBuildData[list.Count]
			};
			TerrainLodCycles[instanceID] = value;
		}
		value.Source = list;
		value.Owner = terrainLod;
		value.Count = list.Count;
		value.Next = 0;
		value.Active = false;
		for (int i = 0; i < list.Count; i++)
		{
			object obj = list[i];
			if (obj == null)
			{
				TerrainLodCycles.Remove(instanceID);
				return false;
			}
			object? value2 = _terrainEntryHeightmapField.GetValue(obj);
			Heightmap val = (Heightmap)((value2 is Heightmap) ? value2 : null);
			if ((Object)(object)val == (Object)null)
			{
				TerrainLodCycles.Remove(instanceID);
				return false;
			}
			value.Entries[i] = obj;
			value.Heightmaps[i] = val;
			value.Offsets[i] = (Vector3)_terrainEntryOffsetField.GetValue(obj);
			value.Data[i] = null;
		}
		return true;
	}

	private static bool BeginTerrainLodCycle(TerrainLodCycle cycle, Vector3 lastPoint)
	{
		//IL_0015: 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_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		HeightmapBuilder instance = HeightmapBuilder.instance;
		WorldGenerator instance2 = WorldGenerator.instance;
		if (instance == null || instance2 == null)
		{
			return false;
		}
		cycle.LastPoint = lastPoint;
		cycle.World = instance2;
		cycle.Next = 0;
		cycle.Active = true;
		for (int i = 0; i < cycle.Count; i++)
		{
			Heightmap val = cycle.Heightmaps[i];
			Vector3 val2 = lastPoint + cycle.Offsets[i];
			cycle.Data[i] = instance.RequestTerrainSync(val2, val.m_width, val.m_scale, val.IsDistantLod, instance2);
		}
		return true;
	}

	private static void CompleteTerrainLodCycle(TerrainLod terrainLod, TerrainLodCycle cycle)
	{
		for (int i = 0; i < cycle.Count; i++)
		{
			_terrainEntryStateSetter(cycle.Entries[i], _terrainDoneState);
			cycle.Data[i] = null;
		}
		_terrainTopStateSetter(terrainLod, _terrainDoneState);
		cycle.Active = false;
		cycle.Next = 0;
	}

	private static void RestoreTerrainCycleForVanilla(TerrainLod terrainLod)
	{
		if ((Object)(object)terrainLod == (Object)null || !TerrainLodCycles.TryGetValue(((Object)terrainLod).GetInstanceID(), out var value) || !value.Active)
		{
			return;
		}
		for (int i = 0; i < value.Count; i++)
		{
			Heightmap val = value.Heightmaps[i];
			HMBuildData val2 = value.Data[i];
			if ((Object)(object)val != (Object)null && val2 != null)
			{
				_heightmapBuildDataRef.Invoke(val) = val2;
				_heightmapCornerBiomesRef.Invoke(val) = val2.m_cornerBiomes;
			}
			if (value.Entries[i] != null)
			{
				_terrainEntryStateSetter(value.Entries[i], _terrainReadyState);
			}
			value.Data[i] = null;
		}
		value.Active = false;
		value.Next = 0;
	}

	private static void TryRestoreTerrainCycleForVanilla(TerrainLod terrainLod)
	{
		try
		{
			RestoreTerrainCycleForVanilla(terrainLod);
		}
		catch (Exception ex)
		{
			Warn("Could not fully return a sliced TerrainLod cycle to vanilla: " + ex.Message);
		}
	}

	private static void TryRestoreAllTerrainCyclesForVanilla()
	{
		foreach (TerrainLodCycle value in TerrainLodCycles.Values)
		{
			if ((Object)(object)value.Owner != (Object)null)
			{
				TryRestoreTerrainCycleForVanilla(value.Owner);
			}
		}
	}

	private static Action<TTarget, int> CreatePrivateEnumSetter<TTarget>(Type declaringType, FieldInfo field)
	{
		DynamicMethod dynamicMethod = new DynamicMethod("BNO_Set_" + declaringType.Name + "_" + field.Name, typeof(void), new Type[2]
		{
			typeof(TTarget),
			typeof(int)
		}, typeof(OptimizerRuntime), skipVisibility: true);
		ILGenerator iLGenerator = dynamicMethod.GetILGenerator();
		iLGenerator.Emit(OpCodes.Ldarg_0);
		if (typeof(TTarget) == typeof(object))
		{
			iLGenerator.Emit(OpCodes.Castclass, declaringType);
		}
		iLGenerator.Emit(OpCodes.Ldarg_1);
		iLGenerator.Emit(OpCodes.Stfld, field);
		iLGenerator.Emit(OpCodes.Ret);
		return (Action<TTarget, int>)dynamicMethod.CreateDelegate(typeof(Action<TTarget, int>));
	}

	private static void InstallTerrainTriggeredClutterSmoothing()
	{
		PatchTranspiler(typeof(ClutterSystem), "ResetGrass", "TerrainTriggeredClutterTranspiler", new Type[2]
		{
			typeof(Vector3),
			typeof(float)
		});
	}

	private static IEnumerable<CodeInstruction> TerrainTriggeredClutterTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		FieldInfo fieldInfo = AccessTools.Field(typeof(ClutterSystem), "m_forceRebuild");
		int num = -1;
		int num2 = 0;
		for (int i = 0; i <= list.Count - 3; i++)
		{
			if (list[i].opcode == OpCodes.Ldarg_0 && list[i + 1].opcode == OpCodes.Ldc_I4_1 && list[i + 2].opcode == OpCodes.Stfld && object.Equals(list[i + 2].operand as FieldInfo, fieldInfo))
			{
				num = i;
				num2++;
			}
		}
		if (fieldInfo == null || num2 != 1)
		{
			Warn("Terrain-triggered clutter IL pattern mismatch in " + __originalMethod?.Name + "; vanilla force rebuild was left unchanged.");
			return list;
		}
		if (num + 3 < list.Count)
		{
			MoveInstructionMetadata(list[num], list[num + 3]);
			MoveInstructionMetadata(list[num + 1], list[num + 3]);
			MoveInstructionMetad