Decompiled source of ValheimPerformanceOverhaul v2.7.2

BepInEx/plugins/ValheimPerformanceOverhaul.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.PostProcessing;
using UnityEngine.UI;
using ValheimPerformanceOverhaul.AI;
using ValheimPerformanceOverhaul.Audio;
using ValheimPerformanceOverhaul.Core;
using ValheimPerformanceOverhaul.ObjectPooling;
using ValheimPerformanceOverhaul.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimPerformanceOverhaul")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimPerformanceOverhaul")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e1d59e72-fa59-450d-94cd-86cc02deca70")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ValheimPerformanceOverhaul
{
	[HarmonyPatch(typeof(Resources), "UnloadUnusedAssets")]
	public static class GCPatches
	{
		private delegate bool InAttackDelegate(Player player);

		private static FieldRef<Character, Vector3> _getMoveDirFast;

		private static InAttackDelegate _inAttackFast;

		private static bool _delegatesInitialized;

		static GCPatches()
		{
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(Character), "m_moveDir");
				MethodInfo methodInfo = AccessTools.Method(typeof(Player), "InAttack", (Type[])null, (Type[])null);
				if (fieldInfo != null && methodInfo != null)
				{
					_getMoveDirFast = AccessTools.FieldRefAccess<Character, Vector3>(fieldInfo);
					_inAttackFast = (InAttackDelegate)Delegate.CreateDelegate(typeof(InAttackDelegate), methodInfo);
					_delegatesInitialized = true;
					Plugin.Log.LogInfo((object)"[GC] Fast delegates initialized successfully.");
				}
				else
				{
					Plugin.Log.LogWarning((object)"[GC] Could not find private fields. GC optimization disabled.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[GC] Failed to initialize delegates: " + ex.Message));
				_delegatesInitialized = false;
			}
		}

		[HarmonyPrefix]
		private static bool PreventGCWhenPlayerIsBusy()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.GcControlEnabled.Value || !_delegatesInitialized)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead())
			{
				return true;
			}
			try
			{
				if (!((Character)localPlayer).IsOnGround() || ((Character)localPlayer).IsSwimming() || ((Character)localPlayer).IsTeleporting())
				{
					return false;
				}
				Vector3 val = _getMoveDirFast.Invoke((Character)(object)localPlayer);
				if (((Vector3)(ref val)).sqrMagnitude > 0.01f)
				{
					return false;
				}
				if (_inAttackFast(localPlayer))
				{
					return false;
				}
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogWarning((object)("[GC] Error checking player state: " + ex.Message));
				}
				return true;
			}
			return true;
		}
	}
	[HarmonyPatch]
	public static class JitPatches
	{
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		[HarmonyPostfix]
		private static void WarmupGameMethods(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Plugin.JitWarmupEnabled.Value)
			{
				return;
			}
			try
			{
				Prepare(AccessTools.Method(typeof(Player), "GetHoverObject", (Type[])null, (Type[])null), "Player.GetHoverObject");
				Prepare(AccessTools.Method(typeof(Player), "GetHoverCreature", (Type[])null, (Type[])null), "Player.GetHoverCreature");
				Prepare(AccessTools.Method(typeof(Player), "GetHoveringPiece", (Type[])null, (Type[])null), "Player.GetHoveringPiece");
				Prepare(AccessTools.Method(typeof(EnvMan), "GetCurrentBiome", (Type[])null, (Type[])null), "EnvMan.GetCurrentBiome");
				Prepare(AccessTools.Method(typeof(InventoryGui), "Show", (Type[])null, (Type[])null), "InventoryGui.Show");
				Prepare(AccessTools.Method(typeof(Character), "Damage", (Type[])null, (Type[])null), "Character.Damage");
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"JIT Warm-up error: {arg}");
			}
		}

		private static void Prepare(MethodInfo method, string methodName)
		{
			if (method != null)
			{
				RuntimeHelpers.PrepareMethod(method.MethodHandle);
			}
		}
	}
	[BepInPlugin("com.Skarif.ValheimPerformanceOverhaul", "Valheim Performance Overhaul", "2.7.0")]
	public class Plugin : BaseUnityPlugin
	{
		private const string PluginGUID = "com.Skarif.ValheimPerformanceOverhaul";

		private const string PluginName = "Valheim Performance Overhaul";

		public const string PluginVersion = "2.7.0";

		private readonly Harmony _harmony = new Harmony("com.Skarif.ValheimPerformanceOverhaul");

		public static ManualLogSource Log;

		public static Plugin Instance;

		public static ConfigEntry<bool> DebugLoggingEnabled;

		public static ConfigEntry<bool> GcControlEnabled;

		public static ConfigEntry<bool> DistanceCullerEnabled;

		public static ConfigEntry<float> CreatureCullDistance;

		public static ConfigEntry<float> PieceCullDistance;

		public static ConfigEntry<bool> CullPhysicsEnabled;

		public static ConfigEntry<bool> AiThrottlingEnabled;

		public static ConfigEntry<string> CullerExclusions;

		public static ConfigEntry<bool> ObjectPoolingEnabled;

		public static ConfigEntry<bool> JitWarmupEnabled;

		public static ConfigEntry<bool> LightCullingEnabled;

		public static ConfigEntry<int> MaxActiveLights;

		public static ConfigEntry<float> LightCullDistance;

		public static ConfigEntry<int> MaxShadowCasters;

		public static ConfigEntry<float> ShadowCullDistance;

		public static ConfigEntry<bool> LightLODEnabled;

		public static ConfigEntry<float> LightLODFullDistance;

		public static ConfigEntry<float> LightLODNoShadowDistance;

		public static ConfigEntry<float> LightLODEmissiveDistance;

		public static ConfigEntry<float> LightLODBillboardDistance;

		public static ConfigEntry<bool> AudioPoolingEnabled;

		public static ConfigEntry<int> AudioPoolSize;

		public static ConfigEntry<bool> GraphicsSettingsEnabled;

		public static ConfigEntry<float> ConfigShadowDistance;

		public static ConfigEntry<int> ConfigShadowResolution;

		public static ConfigEntry<int> ConfigShadowCascades;

		public static ConfigEntry<float> ConfigTerrainQuality;

		public static ConfigEntry<bool> ConfigReflections;

		public static ConfigEntry<bool> ConfigBloom;

		public static ConfigEntry<bool> PieceOptimizationEnabled;

		public static ConfigEntry<float> PieceUpdateInterval;

		public static ConfigEntry<float> PieceColliderDistance;

		public static ConfigEntry<float> PieceSupportCacheDuration;

		public static ConfigEntry<float> PieceUpdateSkipDistance;

		public static ConfigEntry<bool> ParticleOptimizationEnabled;

		public static ConfigEntry<float> ParticleCullDistance;

		public static ConfigEntry<int> MaxActiveParticles;

		public static ConfigEntry<bool> VegetationOptimizationEnabled;

		public static ConfigEntry<float> GrassRenderDistance;

		public static ConfigEntry<float> GrassDensityMultiplier;

		public static ConfigEntry<float> DetailObjectDistance;

		public static ConfigEntry<float> DetailDensity;

		public static ConfigEntry<int> TerrainMaxLOD;

		public static ConfigEntry<bool> AnimatorOptimizationEnabled;

		public static ConfigEntry<bool> MinimapOptimizationEnabled;

		public static ConfigEntry<int> MinimapTextureSize;

		public static ConfigEntry<int> MinimapUpdateInterval;

		public static ConfigEntry<bool> TamedIdleOptimizationEnabled;

		public static ConfigEntry<float> TamedIdleDistanceFromCombat;

		public static ConfigEntry<float> TamedIdleBaseDetectionRadius;

		public static ConfigEntry<float> TamedIdleCheckInterval;

		public static ConfigEntry<bool> LightFlickerOptimizationEnabled;

		public static ConfigEntry<bool> SmokeOptimizationEnabled;

		public static ConfigEntry<float> SmokeLiftForce;

		public static ConfigEntry<bool> EngineQualitySettingsEnabled;

		public static ConfigEntry<int> ParticleRaycastBudget;

		public static ConfigEntry<bool> SkipIntroEnabled;

		public static ConfigEntry<bool> FrameBudgetGuardEnabled;

		public static ConfigEntry<float> FrameBudgetThresholdMs;

		public static ConfigEntry<float> FrameBudgetThrottledDelta;

		public static ConfigEntry<float> FrameBudgetNormalDelta;

		private void Awake()
		{
			_harmony.PatchAll(typeof(WelcomeMessage));
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			SetupConfig();
			Log.LogInfo((object)"Initializing Valheim Performance Overhaul v2.7.0...");
			if (GraphicsSettingsEnabled.Value)
			{
				ApplyImmediateGraphicsSettings();
			}
			Log.LogInfo((object)"Applying Harmony patches...");
			try
			{
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Log.LogInfo((object)"All patches applied successfully.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Error applying patches: " + ex.Message + "\n" + ex.StackTrace));
			}
		}

		private void Start()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			if (ObjectPoolingEnabled.Value)
			{
				ObjectPoolManager.Initialize();
				Log.LogInfo((object)"[ObjectPooling] System initialized.");
			}
			if (AudioPoolingEnabled.Value)
			{
				AudioPoolManager.Initialize();
				Log.LogInfo((object)"[AudioPooling] System initialized.");
			}
			if (DistanceCullerEnabled.Value)
			{
				GameObject val = new GameObject("_VPO_DistanceCullerManager");
				val.AddComponent<DistanceCullerManager>();
				Object.DontDestroyOnLoad((Object)val);
				Log.LogInfo((object)"[DistanceCuller] Manager initialized.");
			}
			if (AiThrottlingEnabled.Value)
			{
				GameObject val2 = new GameObject("_VPO_AIOptimizer");
				val2.AddComponent<AIOptimizer>();
				Object.DontDestroyOnLoad((Object)val2);
				Log.LogInfo((object)"[AI] Optimizer manager initialized.");
			}
			if (FrameBudgetGuardEnabled.Value)
			{
				GameObject val3 = new GameObject("_VPO_FrameBudgetGuard");
				val3.AddComponent<FrameBudgetGuard>();
				Object.DontDestroyOnLoad((Object)val3);
				Log.LogInfo((object)"[FrameBudgetGuard] Initialized.");
			}
			GameObject val4 = new GameObject("_VPO_MemoryManager");
			val4.AddComponent<MemoryManager>();
			Object.DontDestroyOnLoad((Object)val4);
			Log.LogInfo((object)"[MemoryManager] Initialized.");
		}

		private void SetupConfig()
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Expected O, but got Unknown
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Expected O, but got Unknown
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Expected O, but got Unknown
			//IL_02cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Expected O, but got Unknown
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_0312: Expected O, but got Unknown
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_034f: Expected O, but got Unknown
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Expected O, but got Unknown
			//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Expected O, but got Unknown
			//IL_0436: Unknown result type (might be due to invalid IL or missing references)
			//IL_0440: Expected O, but got Unknown
			//IL_047a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0484: Expected O, but got Unknown
			//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Expected O, but got Unknown
			//IL_04e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f2: Expected O, but got Unknown
			//IL_0585: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Expected O, but got Unknown
			//IL_05c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cc: Expected O, but got Unknown
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0609: Expected O, but got Unknown
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0646: Expected O, but got Unknown
			//IL_0699: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a3: Expected O, but got Unknown
			//IL_06cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d7: Expected O, but got Unknown
			//IL_072a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0734: Expected O, but got Unknown
			//IL_0767: Unknown result type (might be due to invalid IL or missing references)
			//IL_0771: Expected O, but got Unknown
			//IL_07a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ae: Expected O, but got Unknown
			//IL_07e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_07eb: Expected O, but got Unknown
			//IL_0812: Unknown result type (might be due to invalid IL or missing references)
			//IL_081c: Expected O, but got Unknown
			//IL_0896: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a0: Expected O, but got Unknown
			//IL_08c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_08d2: Expected O, but got Unknown
			//IL_0925: Unknown result type (might be due to invalid IL or missing references)
			//IL_092f: Expected O, but got Unknown
			//IL_0962: Unknown result type (might be due to invalid IL or missing references)
			//IL_096c: Expected O, but got Unknown
			//IL_099f: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a9: Expected O, but got Unknown
			//IL_0a1c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a26: Expected O, but got Unknown
			//IL_0a76: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a80: Expected O, but got Unknown
			//IL_0af3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0afd: Expected O, but got Unknown
			//IL_0b30: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b3a: Expected O, but got Unknown
			//IL_0b6d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b77: Expected O, but got Unknown
			DebugLoggingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enable Debug Logging", false, "Enables detailed diagnostic logs for troubleshooting.");
			GcControlEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. GC Control", "Enabled", true, "Prevents garbage collection during combat or movement to reduce stuttering.");
			DistanceCullerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enabled", true, "Disables or throttles logic for distant objects to improve performance.");
			CreatureCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3. Distance Culler", "Creature Cull Distance", 80f, new ConfigDescription("Distance at which creatures are put to sleep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 200f), Array.Empty<object>()));
			PieceCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3. Distance Culler", "Piece Cull Distance", 100f, new ConfigDescription("Distance at which build pieces are put to sleep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 300f), Array.Empty<object>()));
			CullPhysicsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enable Physics Culling", true, "Disables Rigidbody physics on distant objects.");
			AiThrottlingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enable AI Throttling", true, "Reduces how often distant AI updates to save CPU cycles.");
			CullerExclusions = ((BaseUnityPlugin)this).Config.Bind<string>("3. Distance Culler", "Exclusions", "TombStone,portal_wood", "Comma-separated list of prefab names to NEVER cull.");
			ObjectPoolingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Object Pooling", "Enabled", true, "Reuses ItemDrop objects to reduce instantiation overhead.");
			JitWarmupEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5. JIT Warm-up", "Enabled", true, "Pre-compiles critical methods at game start to prevent initial stuttering.");
			LightCullingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Light Culling", "Enabled", true, "Disables distant light sources to improve performance. MAJOR FPS improvement!");
			MaxActiveLights = ((BaseUnityPlugin)this).Config.Bind<int>("6. Light Culling", "Max Active Lights", 15, new ConfigDescription("Maximum number of active lights near the player.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 50), Array.Empty<object>()));
			LightCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "Light Cull Distance", 60f, new ConfigDescription("Maximum distance for active lights.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>()));
			MaxShadowCasters = ((BaseUnityPlugin)this).Config.Bind<int>("6. Light Culling", "Max Shadow Casters", 5, new ConfigDescription("Maximum number of lights that can cast shadows simultaneously.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 15), Array.Empty<object>()));
			ShadowCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "Shadow Cull Distance", 30f, new ConfigDescription("Distance at which shadows are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 80f), Array.Empty<object>()));
			LightLODEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Light Culling", "Enable Light LOD System", true, "Enables Level of Detail system for lights. MASSIVE FPS improvement!");
			LightLODFullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Full Light Distance", 20f, new ConfigDescription("Distance for full quality light with shadows.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 50f), Array.Empty<object>()));
			LightLODNoShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD No Shadow Distance", 40f, new ConfigDescription("Distance at which shadows are removed but light remains.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 80f), Array.Empty<object>()));
			LightLODEmissiveDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Emissive Distance", 70f, new ConfigDescription("Distance at which light is replaced with emissive material.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 120f), Array.Empty<object>()));
			LightLODBillboardDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Billboard Distance", 100f, new ConfigDescription("Distance at which emissive is replaced with simple billboard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 200f), Array.Empty<object>()));
			AudioPoolingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("7. Audio Optimization", "Enabled", true, "Reuses sound effect objects to reduce allocations.");
			AudioPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("7. Audio Optimization", "Total Pool Size", 32, new ConfigDescription("Total number of reusable AudioSource components.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16, 128), Array.Empty<object>()));
			GraphicsSettingsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enabled", true, "Enables advanced graphics settings module.");
			ConfigShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("8. Graphics Settings", "Shadow Distance", 50f, new ConfigDescription("Maximum distance for shadow rendering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>()));
			ConfigShadowResolution = ((BaseUnityPlugin)this).Config.Bind<int>("8. Graphics Settings", "Shadow Resolution", 512, new ConfigDescription("Shadow resolution quality.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 512, 1024, 2048, 4096 }), Array.Empty<object>()));
			ConfigShadowCascades = ((BaseUnityPlugin)this).Config.Bind<int>("8. Graphics Settings", "Shadow Cascades", 1, new ConfigDescription("Number of shadow cascades.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>()));
			ConfigTerrainQuality = ((BaseUnityPlugin)this).Config.Bind<float>("8. Graphics Settings", "Terrain Quality Multiplier", 0.7f, new ConfigDescription("Terrain detail quality.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			ConfigReflections = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enable Reflections", false, "Enables screen-space reflections.");
			ConfigBloom = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enable Bloom", false, "Enables bloom glow effect.");
			PieceOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Piece Optimization", "Enabled", true, "Optimizes building piece updates and collision detection.");
			PieceUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Update Interval (seconds)", 2f, new ConfigDescription("How often building pieces update their state.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>()));
			PieceColliderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Collider Disable Distance", 80f, new ConfigDescription("Distance at which building colliders are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 200f), Array.Empty<object>()));
			PieceSupportCacheDuration = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Support Cache Duration (seconds)", 5f, new ConfigDescription("How long to cache building support calculations.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>()));
			PieceUpdateSkipDistance = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Update Skip Distance", 50f, new ConfigDescription("Distance at which building pieces stop updating entirely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 150f), Array.Empty<object>()));
			ParticleOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("11. Particle Optimization", "Enabled", true, "Optimizes particle systems (fire, smoke, sparks).");
			ParticleCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("11. Particle Optimization", "Particle Cull Distance", 50f, new ConfigDescription("Distance at which particle systems are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 100f), Array.Empty<object>()));
			MaxActiveParticles = ((BaseUnityPlugin)this).Config.Bind<int>("11. Particle Optimization", "Max Active Particle Systems", 30, new ConfigDescription("Maximum number of active particle systems.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>()));
			VegetationOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("12. Vegetation Optimization", "Enabled", true, "Optimizes grass, bushes and terrain details.");
			GrassRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Grass Render Distance", 60f, new ConfigDescription("Distance at which grass is rendered.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 120f), Array.Empty<object>()));
			GrassDensityMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Grass Density Multiplier", 0.7f, new ConfigDescription("Grass density. 1.0 = vanilla, 0.5 = half grass.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1f), Array.Empty<object>()));
			DetailObjectDistance = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Detail Object Distance", 80f, new ConfigDescription("Distance for small objects (stones, sticks).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 150f), Array.Empty<object>()));
			DetailDensity = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Detail Density", 0.7f, new ConfigDescription("Density of detail objects.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1f), Array.Empty<object>()));
			TerrainMaxLOD = ((BaseUnityPlugin)this).Config.Bind<int>("12. Vegetation Optimization", "Terrain Max LOD", 1, new ConfigDescription("Maximum LOD level for terrain.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>()));
			AnimatorOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("13. Animator Optimization", "Enabled", true, "Optimizes character animations.");
			MinimapOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("15. Minimap Optimization", "Enabled", true, "Reduces minimap texture size and update frequency.");
			MinimapTextureSize = ((BaseUnityPlugin)this).Config.Bind<int>("15. Minimap Optimization", "Minimap Texture Size", 1024, new ConfigDescription("Minimap texture resolution.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[3] { 512, 1024, 2048 }), Array.Empty<object>()));
			MinimapUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<int>("15. Minimap Optimization", "Update Interval (frames)", 2, new ConfigDescription("Update minimap every N frames.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			TamedIdleOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("16. Tamed Mob Idle Optimization", "Enabled", true, "Enables BASE IDLE MODE for tamed mobs on base. Dramatically reduces CPU load from idle tamed creatures.");
			TamedIdleDistanceFromCombat = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Idle Distance From Combat (seconds)", 5f, new ConfigDescription("Time after combat before mob can enter idle mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 30f), Array.Empty<object>()));
			TamedIdleBaseDetectionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Base Detection Radius", 30f, new ConfigDescription("Radius to detect player structures (base detection).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 60f), Array.Empty<object>()));
			TamedIdleCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Check Interval (seconds)", 1f, new ConfigDescription("How often to check if mob should enter/exit idle mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>()));
			LightFlickerOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("17. Light Flicker Optimization", "Enabled", true, "Fixes light intensity to base value, eliminating Shadow Map recalculation every frame from fires and torches. RECOMMENDED: Large performance gain on bases with many light sources.");
			SmokeOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("18. Smoke Physics Optimization", "Enabled", true, "Replaces complex smoke aerodynamics with simple linear interpolation. Dramatic CPU savings on large bases with many campfires.");
			SmokeLiftForce = ((BaseUnityPlugin)this).Config.Bind<float>("18. Smoke Physics Optimization", "Smoke Lift Force", 3.5f, new ConfigDescription("Upward force applied to smoke particles with simplified physics. Higher = faster rising smoke.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			EngineQualitySettingsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("19. Engine Quality Settings", "Enabled", true, "Applies low-level Unity QualitySettings tweaks: disables soft particles and soft vegetation, reduces particle raycast budget. Minor visual change, significant GPU gain.");
			ParticleRaycastBudget = ((BaseUnityPlugin)this).Config.Bind<int>("19. Engine Quality Settings", "Particle Raycast Budget", 1024, new ConfigDescription("Max number of particle ray-casts per frame. Vanilla default is 4096. Lower = less CPU.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 4096), Array.Empty<object>()));
			SkipIntroEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("20. Skip Intro", "Enabled", true, "Skips Iron Gate and Coffee Stain logo screens on game startup. Saves 5–10 seconds per launch.");
			FrameBudgetGuardEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("21. Frame Budget Guard", "Enabled", true, "Dynamically limits Time.maximumDeltaTime when heavy frame spikes are detected, preventing the Unity physics 'Death Spiral'. Converts hard freezes into brief slow-motion.");
			FrameBudgetThresholdMs = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Freeze Threshold (ms)", 28f, new ConfigDescription("1% Low frametime threshold in milliseconds. If worst frames exceed this, throttling activates. 28ms ≈ below 36 FPS on worst frames.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 100f), Array.Empty<object>()));
			FrameBudgetThrottledDelta = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Throttled MaxDeltaTime", 0.045f, new ConfigDescription("Time.maximumDeltaTime when throttling is active (freeze detected). Lower = physics accuracy is sacrificed more to preserve smoothness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 0.1f), Array.Empty<object>()));
			FrameBudgetNormalDelta = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Normal MaxDeltaTime", 0.07f, new ConfigDescription("Time.maximumDeltaTime during normal gameplay. Unity default is 0.3333.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.03f, 0.2f), Array.Empty<object>()));
		}

		private void ApplyImmediateGraphicsSettings()
		{
			try
			{
				QualitySettings.shadowDistance = ConfigShadowDistance.Value;
				switch (ConfigShadowResolution.Value)
				{
				case 512:
					QualitySettings.shadowResolution = (ShadowResolution)0;
					break;
				case 1024:
					QualitySettings.shadowResolution = (ShadowResolution)1;
					break;
				case 2048:
					QualitySettings.shadowResolution = (ShadowResolution)2;
					break;
				case 4096:
					QualitySettings.shadowResolution = (ShadowResolution)3;
					break;
				default:
					QualitySettings.shadowResolution = (ShadowResolution)0;
					break;
				}
				QualitySettings.shadowCascades = ConfigShadowCascades.Value;
				Log.LogInfo((object)"[Graphics] Applied immediate graphics settings.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[Graphics] Failed to apply settings: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			Log.LogInfo((object)"Unpatching all Valheim Performance Overhaul methods.");
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public class MemoryManager : MonoBehaviour
	{
		private float _cleanupTimer;

		private const float CLEANUP_INTERVAL = 60f;

		private float _forceGCTimer;

		private const float FORCE_GC_INTERVAL = 300f;

		public static MemoryManager Instance { get; private set; }

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			Plugin.Log.LogInfo((object)"[MemoryManager] Initialized");
		}

		private void Update()
		{
			_cleanupTimer += Time.deltaTime;
			_forceGCTimer += Time.deltaTime;
			if (_cleanupTimer >= 60f)
			{
				_cleanupTimer = 0f;
				PerformLightCleanup();
			}
			if (_forceGCTimer >= 300f)
			{
				_forceGCTimer = 0f;
				if (ShouldPerformHeavyCleanup())
				{
					PerformHeavyCleanup();
				}
			}
		}

		private bool ShouldPerformHeavyCleanup()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Character)localPlayer).IsAttached() && !localPlayer.IsSleeping())
			{
				return ((Character)localPlayer).InPlaceMode();
			}
			return true;
		}

		private void PerformLightCleanup()
		{
			int num = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576);
			Resources.UnloadUnusedAssets();
			int num2 = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576);
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"[MemoryManager] Light cleanup: {num}MB -> {num2}MB");
			}
		}

		private void PerformHeavyCleanup()
		{
			int num = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576);
			Resources.UnloadUnusedAssets();
			GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
			GC.WaitForPendingFinalizers();
			GC.Collect();
			int num2 = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576);
			Plugin.Log.LogInfo((object)$"[MemoryManager] Heavy cleanup: {num}MB -> {num2}MB");
		}

		private void OnDestroy()
		{
			Instance = null;
		}
	}
	public class DistanceCuller : MonoBehaviour
	{
		private readonly List<MonoBehaviour> _culledComponents = new List<MonoBehaviour>();

		private readonly List<Rigidbody> _trackedRigidbodies = new List<Rigidbody>();

		private ZNetView _zNetView;

		private bool _isCulled;

		private bool _isCharacter;

		private Transform _transform;

		public float CullDistance = 80f;

		private float _cullDistanceSqr;

		private float _wakeUpDistanceSqr;

		private const float HYSTERESIS = 15f;

		private void Awake()
		{
			_transform = ((Component)this).transform;
			_zNetView = ((Component)this).GetComponent<ZNetView>();
			if ((Object)(object)_zNetView == (Object)null || !_zNetView.IsValid())
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			_isCharacter = (Object)(object)((Component)this).GetComponent<Character>() != (Object)null;
			try
			{
				_cullDistanceSqr = (CullDistance + 15f) * (CullDistance + 15f);
				_wakeUpDistanceSqr = (CullDistance - 15f) * (CullDistance - 15f);
				CollectComponents();
				if (Plugin.CullPhysicsEnabled.Value && !_isCharacter)
				{
					CollectRigidbodies();
				}
				DistanceCullerManager.Instance?.RegisterCuller(this);
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogError((object)("[DistanceCuller] Error in Awake: " + ex.Message));
				}
				Object.Destroy((Object)(object)this);
			}
		}

		private void CollectComponents()
		{
			MonoBehaviour[] componentsInChildren = ((Component)this).GetComponentsInChildren<MonoBehaviour>(true);
			if (componentsInChildren == null)
			{
				return;
			}
			MonoBehaviour[] array = componentsInChildren;
			foreach (MonoBehaviour val in array)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)this) && !(val is ZNetView) && !(val is ZSyncTransform) && !(val is DistanceCuller) && !(val is Character) && !(val is Humanoid) && (!Plugin.AiThrottlingEnabled.Value || !(val is BaseAI)))
				{
					_culledComponents.Add(val);
				}
			}
		}

		private void CollectRigidbodies()
		{
			Rigidbody[] componentsInChildren = ((Component)this).GetComponentsInChildren<Rigidbody>(true);
			if (componentsInChildren == null)
			{
				return;
			}
			Rigidbody[] array = componentsInChildren;
			foreach (Rigidbody val in array)
			{
				if ((Object)(object)val != (Object)null && !_trackedRigidbodies.Contains(val))
				{
					_trackedRigidbodies.Add(val);
				}
			}
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"[DistanceCuller] Collected {_trackedRigidbodies.Count} Rigidbodies on {((Object)((Component)this).gameObject).name}");
			}
		}

		public void ManagerUpdate(IReadOnlyList<Player> players)
		{
			if (players == null || players.Count == 0)
			{
				if (_isCulled)
				{
					SetComponentsEnabled(enabled: true);
				}
			}
			else
			{
				float minPlayerDistanceSqr = GetMinPlayerDistanceSqr(players);
				bool shouldCull = DetermineCullingState(minPlayerDistanceSqr);
				ApplyOwnershipLogic(shouldCull);
			}
		}

		private float GetMinPlayerDistanceSqr(IReadOnlyList<Player> players)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			Vector3 position = _transform.position;
			for (int i = 0; i < players.Count; i++)
			{
				Player val = players[i];
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null))
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
					}
				}
			}
			return num;
		}

		private bool DetermineCullingState(float distSqr)
		{
			if (!_isCulled)
			{
				return distSqr > _cullDistanceSqr;
			}
			return distSqr > _wakeUpDistanceSqr;
		}

		private void ApplyOwnershipLogic(bool shouldCull)
		{
			if ((Object)(object)_zNetView == (Object)null)
			{
				return;
			}
			if (_zNetView.IsOwner())
			{
				if (_isCulled != shouldCull)
				{
					SetComponentsEnabled(!shouldCull);
				}
			}
			else if (_isCulled)
			{
				SetComponentsEnabled(enabled: true);
			}
		}

		private void SetComponentsEnabled(bool enabled)
		{
			if (_isCulled == !enabled)
			{
				return;
			}
			_isCulled = !enabled;
			for (int num = _culledComponents.Count - 1; num >= 0; num--)
			{
				MonoBehaviour val = _culledComponents[num];
				if ((Object)(object)val == (Object)null)
				{
					_culledComponents.RemoveAt(num);
				}
				else if (((Behaviour)val).enabled != enabled)
				{
					try
					{
						((Behaviour)val).enabled = enabled;
					}
					catch (Exception ex)
					{
						if (Plugin.DebugLoggingEnabled.Value)
						{
							Plugin.Log.LogWarning((object)("[DistanceCuller] Failed to set " + ((object)val).GetType().Name + ": " + ex.Message));
						}
					}
				}
			}
			if (!Plugin.CullPhysicsEnabled.Value || _isCharacter)
			{
				return;
			}
			for (int num2 = _trackedRigidbodies.Count - 1; num2 >= 0; num2--)
			{
				Rigidbody val2 = _trackedRigidbodies[num2];
				if ((Object)(object)val2 == (Object)null)
				{
					_trackedRigidbodies.RemoveAt(num2);
				}
				else
				{
					try
					{
						if (enabled)
						{
							val2.WakeUp();
						}
						else if (!val2.isKinematic && !val2.IsSleeping())
						{
							val2.Sleep();
						}
					}
					catch (Exception ex2)
					{
						if (Plugin.DebugLoggingEnabled.Value)
						{
							Plugin.Log.LogWarning((object)("[DistanceCuller] Rigidbody error: " + ex2.Message));
						}
					}
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				DistanceCullerManager.Instance?.UnregisterCuller(this);
				if (_isCulled)
				{
					SetComponentsEnabled(enabled: true);
				}
				_culledComponents.Clear();
				_trackedRigidbodies.Clear();
			}
			catch (Exception ex)
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogError((object)("[DistanceCuller] OnDestroy error: " + ex.Message));
				}
			}
		}
	}
	public class DistanceCullerManager : MonoBehaviour
	{
		private static readonly List<Player> _globalPlayerCache = new List<Player>();

		private readonly List<DistanceCuller> _cullers = new List<DistanceCuller>(512);

		private float _playerUpdateTimer;

		private float _cullingUpdateTimer;

		private const float PLAYER_UPDATE_INTERVAL = 1f;

		private const float CULLING_UPDATE_INTERVAL = 2f;

		public static DistanceCullerManager Instance { get; private set; }

		public static IReadOnlyList<Player> Players => _globalPlayerCache;

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)this);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			Plugin.Log.LogInfo((object)"[DistanceCullerManager] Initialized.");
		}

		public void RegisterCuller(DistanceCuller culler)
		{
			if ((Object)(object)culler != (Object)null && !_cullers.Contains(culler))
			{
				_cullers.Add(culler);
			}
		}

		public void UnregisterCuller(DistanceCuller culler)
		{
			int num = _cullers.IndexOf(culler);
			if (num >= 0)
			{
				int index = _cullers.Count - 1;
				_cullers[num] = _cullers[index];
				_cullers.RemoveAt(index);
			}
		}

		private void Update()
		{
			_playerUpdateTimer += Time.deltaTime;
			_cullingUpdateTimer += Time.deltaTime;
			if (_playerUpdateTimer >= 1f)
			{
				_playerUpdateTimer = 0f;
				_globalPlayerCache.Clear();
				List<Player> allPlayers = Player.GetAllPlayers();
				if (allPlayers != null)
				{
					_globalPlayerCache.AddRange(allPlayers);
				}
			}
			if (!(_cullingUpdateTimer >= 2f))
			{
				return;
			}
			_cullingUpdateTimer = 0f;
			if (!Plugin.DistanceCullerEnabled.Value || _globalPlayerCache.Count == 0)
			{
				return;
			}
			for (int num = _cullers.Count - 1; num >= 0; num--)
			{
				DistanceCuller distanceCuller = _cullers[num];
				if ((Object)(object)distanceCuller == (Object)null)
				{
					int index = _cullers.Count - 1;
					_cullers[num] = _cullers[index];
					_cullers.RemoveAt(index);
				}
				else
				{
					distanceCuller.ManagerUpdate(_globalPlayerCache);
				}
			}
		}

		private void OnDestroy()
		{
			_cullers.Clear();
			Instance = null;
		}
	}
	[HarmonyPatch]
	public static class GraphicsPatches
	{
		private static readonly FieldInfo _heightmapsListField = AccessTools.Field(typeof(Heightmap), "s_heightmaps");

		[HarmonyPatch(typeof(GameCamera), "Awake")]
		[HarmonyPostfix]
		private static void ApplyPostProcessingSettings(GameCamera __instance)
		{
			if (!Plugin.GraphicsSettingsEnabled.Value)
			{
				return;
			}
			PostProcessingBehaviour component = ((Component)__instance).GetComponent<PostProcessingBehaviour>();
			if (!((Object)(object)component == (Object)null) && !((Object)(object)component.profile == (Object)null))
			{
				PostProcessingProfile profile = component.profile;
				((PostProcessingModel)profile.bloom).enabled = Plugin.ConfigBloom.Value;
				((PostProcessingModel)profile.screenSpaceReflection).enabled = Plugin.ConfigReflections.Value;
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)"[Graphics] Applied post-processing settings.");
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned")]
		[HarmonyPostfix]
		private static void ApplyInitialTerrainSettings()
		{
			if (!Plugin.GraphicsSettingsEnabled.Value || _heightmapsListField == null || !(_heightmapsListField.GetValue(null) is List<Heightmap> list))
			{
				return;
			}
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"[Graphics] Applying terrain quality to {list.Count} existing heightmaps...");
			}
			float num = Mathf.Clamp(Plugin.ConfigTerrainQuality.Value, 0.1f, 2f);
			float heightmapPixelError = 5f / num;
			foreach (Heightmap item in list)
			{
				if ((Object)(object)item != (Object)null)
				{
					Terrain component = ((Component)item).GetComponent<Terrain>();
					if ((Object)(object)component != (Object)null)
					{
						component.heightmapPixelError = heightmapPixelError;
					}
				}
			}
		}
	}
	public static class AnimatorOptimizer
	{
		[HarmonyPatch(typeof(Character), "Awake")]
		public static class Character_Awake_Patch
		{
			[HarmonyPostfix]
			private static void Postfix(Character __instance)
			{
				if (!((Object)(object)__instance == (Object)null) && Plugin.AnimatorOptimizationEnabled.Value && (Object)(object)((Component)__instance).GetComponent<CharacterAnimatorOptimizer>() == (Object)null)
				{
					((Component)__instance).gameObject.AddComponent<CharacterAnimatorOptimizer>();
				}
			}
		}
	}
	public class CharacterAnimatorOptimizer : MonoBehaviour
	{
		private Character _character;

		private Animator _animator;

		private ZNetView _nview;

		private float _checkTimer;

		private const float CHECK_INTERVAL = 1f;

		private const float CULL_DIST_SQR = 3600f;

		private const float FAR_CULL_DIST_SQR = 10000f;

		private bool _isFullyCulled;

		private bool _isPartiallyCulled;

		private void Awake()
		{
			_character = ((Component)this).GetComponent<Character>();
			_animator = ((Component)this).GetComponent<Animator>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			_checkTimer = Random.Range(0f, 1f);
		}

		private void FixedUpdate()
		{
			_checkTimer += Time.fixedDeltaTime;
			if (!(_checkTimer < 1f))
			{
				_checkTimer = 0f;
				Optimize();
			}
		}

		private void Optimize()
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_character == (Object)null || (Object)(object)_animator == (Object)null)
			{
				Object.Destroy((Object)(object)this);
			}
			else
			{
				if (_character.IsPlayer() || ((Object)(object)_nview != (Object)null && !_nview.IsValid()) || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				Vector3 val = ((Component)_character).transform.position - ((Component)Player.m_localPlayer).transform.position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude > 10000f)
				{
					if (!_isFullyCulled)
					{
						((Behaviour)_animator).enabled = false;
						_isFullyCulled = true;
						_isPartiallyCulled = false;
						if (Plugin.DebugLoggingEnabled.Value)
						{
							Plugin.Log.LogInfo((object)$"[Animator] Fully disabled for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m");
						}
					}
				}
				else if (sqrMagnitude > 3600f)
				{
					if (!_isPartiallyCulled || _isFullyCulled)
					{
						((Behaviour)_animator).enabled = true;
						_animator.cullingMode = (AnimatorCullingMode)2;
						_isPartiallyCulled = true;
						_isFullyCulled = false;
						if (Plugin.DebugLoggingEnabled.Value)
						{
							Plugin.Log.LogInfo((object)$"[Animator] CullCompletely for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m");
						}
					}
				}
				else if (_isPartiallyCulled || _isFullyCulled)
				{
					((Behaviour)_animator).enabled = true;
					_animator.cullingMode = (AnimatorCullingMode)0;
					_isPartiallyCulled = false;
					_isFullyCulled = false;
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)$"[Animator] Full animation for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m");
					}
				}
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)_animator != (Object)null)
			{
				((Behaviour)_animator).enabled = true;
				_animator.cullingMode = (AnimatorCullingMode)0;
			}
		}
	}
	[HarmonyPatch]
	public static class MinimapOptimizer
	{
		private static FieldInfo _smallRootField;

		private static FieldInfo _largeRootField;

		private static bool _fieldsFound;

		static MinimapOptimizer()
		{
			_smallRootField = AccessTools.Field(typeof(Minimap), "m_smallRoot");
			_largeRootField = AccessTools.Field(typeof(Minimap), "m_largeRoot");
			_fieldsFound = false;
			if (_smallRootField != null && _largeRootField != null)
			{
				_fieldsFound = true;
				return;
			}
			if (_smallRootField == null)
			{
				_smallRootField = AccessTools.Field(typeof(Minimap), "m_smallMapPanel");
			}
			if (_largeRootField == null)
			{
				_largeRootField = AccessTools.Field(typeof(Minimap), "m_largeMapPanel");
			}
			_fieldsFound = _smallRootField != null && _largeRootField != null;
			if (!_fieldsFound)
			{
				Plugin.Log.LogWarning((object)"[MinimapOptimizer] Could not find Minimap root fields. Optimization disabled.");
			}
		}

		[HarmonyPatch(typeof(Minimap), "Update")]
		[HarmonyPrefix]
		private static bool Minimap_Update_Prefix(Minimap __instance)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			if (!Plugin.MinimapOptimizationEnabled.Value)
			{
				return true;
			}
			if (!_fieldsFound)
			{
				return true;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return true;
			}
			if ((int)__instance.m_mode == 2)
			{
				return true;
			}
			if ((int)__instance.m_mode == 1)
			{
				GameObject val = (GameObject)_smallRootField.GetValue(__instance);
				if ((Object)(object)val != (Object)null && !val.activeInHierarchy)
				{
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(Minimap), "UpdateDynamicPins")]
		[HarmonyPrefix]
		private static bool UpdateDynamicPins_Prefix(Minimap __instance)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.MinimapOptimizationEnabled.Value)
			{
				return true;
			}
			if ((int)__instance.m_mode == 0)
			{
				return false;
			}
			return true;
		}
	}
}
namespace ValheimPerformanceOverhaul.UI
{
	public static class WelcomeMessage
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static UnityAction <>9__2_0;

			internal void <ShowWelcomePanel>b__2_0()
			{
				Application.OpenURL("https://discord.com/users/1084209564653727804");
			}
		}

		private static bool _shown = false;

		private static readonly string PREFS_KEY = "VPO_HideWelcome_v2.7.0";

		[HarmonyPatch(typeof(FejdStartup), "Start")]
		[HarmonyPostfix]
		public static void ShowWelcomePanel()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: 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)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0402: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0420: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Unknown result type (might be due to invalid IL or missing references)
			//IL_048e: Unknown result type (might be due to invalid IL or missing references)
			//IL_049d: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_04dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0502: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Expected O, but got Unknown
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0543: Unknown result type (might be due to invalid IL or missing references)
			//IL_0552: Unknown result type (might be due to invalid IL or missing references)
			//IL_0561: Unknown result type (might be due to invalid IL or missing references)
			//IL_057e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d6: Expected O, but got Unknown
			//IL_0327: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Expected O, but got Unknown
			if (_shown || PlayerPrefs.GetInt(PREFS_KEY, 0) == 1)
			{
				return;
			}
			_shown = true;
			GameObject canvasGO = new GameObject("WelcomeCanvas_VPO");
			canvasGO.layer = 5;
			Canvas obj = canvasGO.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 9999;
			canvasGO.AddComponent<CanvasScaler>();
			canvasGO.AddComponent<GraphicRaycaster>();
			Object.DontDestroyOnLoad((Object)(object)canvasGO);
			GameObject val = MakeRect("Background", canvasGO.transform, new Color(0.05f, 0.05f, 0.05f, 0.96f));
			RectTransform component = val.GetComponent<RectTransform>();
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			component.anchorMax = val2;
			component.anchorMin = val2;
			component.sizeDelta = new Vector2(550f, 480f);
			component.anchoredPosition = Vector2.zero;
			GameObject val3 = MakeRect("Header", val.transform, new Color(0.1f, 0.1f, 0.1f, 1f));
			Stretch(val3, new Vector2(0f, 0.86f), Vector2.one);
			MakeText("Title", val3.transform, "ValheimPerformanceOverhaul", 22, new Color(1f, 0.85f, 0.4f), (FontStyle)1);
			Text obj2 = MakeText("Body", val.transform, "Hi! Thank you for using my mod.\nThe mod is in beta, so I'd love to hear any feedback:\nbugs, suggestions, logs - anything.\n\nПривет! Спасибо за использование моего мода.\nМод находится в бете, поэтому я буду рад любым отзывам:\nбагам, предложениям, логам - чему угодно.\n\nMy Discord account / Мой аккаунт Discord:", 15, Color.white, (FontStyle)0);
			obj2.lineSpacing = 1.2f;
			obj2.alignment = (TextAnchor)1;
			SetAnchors(((Component)obj2).gameObject, new Vector2(0f, 0.35f), new Vector2(1f, 0.86f), new Vector2(20f, 0f), new Vector2(-20f, -15f));
			GameObject val4 = MakeRect("Link", val.transform, Color.clear);
			SetAnchors(val4, new Vector2(0f, 0.25f), new Vector2(1f, 0.35f), new Vector2(20f, 0f), new Vector2(-20f, 0f));
			MakeText("LinkText", val4.transform, "https://discord.com/users/1084209564653727804 (skarif_q)", 15, new Color(0.35f, 0.7f, 1f), (FontStyle)0);
			RectTransform component2 = MakeRect("Underline", val4.transform, new Color(0.35f, 0.7f, 1f)).GetComponent<RectTransform>();
			component2.anchorMin = new Vector2(0.05f, 0f);
			component2.anchorMax = new Vector2(0.95f, 0f);
			component2.sizeDelta = new Vector2(0f, 1.5f);
			component2.anchoredPosition = new Vector2(0f, 3f);
			Image component3 = val4.GetComponent<Image>();
			Color highlight = new Color(0.35f, 0.7f, 1f, 0.15f);
			Color pressed = new Color(0.35f, 0.7f, 1f, 0.3f);
			object obj3 = <>c.<>9__2_0;
			if (obj3 == null)
			{
				UnityAction val5 = delegate
				{
					Application.OpenURL("https://discord.com/users/1084209564653727804");
				};
				<>c.<>9__2_0 = val5;
				obj3 = (object)val5;
			}
			MakeButton(val4, component3, highlight, pressed, (UnityAction)obj3);
			MakeSeparator("Sep1", val.transform, 0.22f);
			MakeSeparator("Sep2", val.transform, 0.14f);
			bool dontShow = false;
			SetPivotAnchor(((Component)MakeText("Label", val.transform, "Don't show anymore / Больше не показывать", 13, new Color(0.75f, 0.75f, 0.75f), (FontStyle)0)).gameObject, new Vector2(0.5f, 0f), new Vector2(280f, 30f), new Vector2(20f, 85f));
			GameObject val6 = MakeRect("Box", val.transform, new Color(0.25f, 0.25f, 0.25f, 1f));
			SetPivotAnchor(val6, new Vector2(0.5f, 0f), new Vector2(18f, 18f), new Vector2(-135f, 85f));
			GameObject tickGO = ((Component)MakeText("Tick", val6.transform, "v", 13, new Color(0.35f, 0.7f, 1f), (FontStyle)1)).gameObject;
			tickGO.SetActive(false);
			GameObject obj4 = MakeRect("CheckHit", val.transform, Color.clear);
			SetPivotAnchor(obj4, new Vector2(0.5f, 0f), new Vector2(320f, 30f), new Vector2(0f, 85f));
			Image boxImg = val6.GetComponent<Image>();
			MakeButton(obj4, obj4.GetComponent<Image>(), new Color(1f, 1f, 1f, 0.05f), new Color(1f, 1f, 1f, 0.1f), (UnityAction)delegate
			{
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				dontShow = !dontShow;
				tickGO.SetActive(dontShow);
				((Graphic)boxImg).color = (dontShow ? new Color(0.1f, 0.25f, 0.45f, 1f) : new Color(0.25f, 0.25f, 0.25f, 1f));
			});
			GameObject val7 = MakeRect("Close", val.transform, new Color(0.65f, 0.15f, 0.15f, 1f));
			SetPivotAnchor(val7, new Vector2(0.5f, 0f), new Vector2(140f, 34f), new Vector2(0f, 20f));
			MakeText("CloseText", val7.transform, "Close / Закрыть", 15, Color.white, (FontStyle)0);
			MakeButton(val7, val7.GetComponent<Image>(), new Color(0.85f, 0.25f, 0.25f, 1f), new Color(0.45f, 0.08f, 0.08f, 1f), (UnityAction)delegate
			{
				if (dontShow)
				{
					PlayerPrefs.SetInt(PREFS_KEY, 1);
				}
				Object.Destroy((Object)(object)canvasGO);
			});
		}

		private static GameObject MakeRect(string name, Transform parent, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			GameObject val = new GameObject(name)
			{
				layer = 5
			};
			val.transform.SetParent(parent, false);
			val.AddComponent<RectTransform>();
			((Graphic)val.AddComponent<Image>()).color = color;
			return val;
		}

		private static Text MakeText(string name, Transform parent, string text, int size, Color color, FontStyle style = 0)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0042: 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_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.layer = 5;
			val.transform.SetParent(parent, false);
			val.AddComponent<RectTransform>();
			Text obj = val.AddComponent<Text>();
			obj.text = text;
			obj.font = GetFont();
			obj.fontSize = size;
			((Graphic)obj).color = color;
			obj.fontStyle = style;
			obj.alignment = (TextAnchor)4;
			Stretch(val, Vector2.zero, Vector2.one);
			return obj;
		}

		private static void MakeButton(GameObject go, Image target, Color highlight, Color pressed, UnityAction action)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Button obj = go.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)target;
			ColorBlock colors = ((Selectable)obj).colors;
			((ColorBlock)(ref colors)).normalColor = ((Graphic)target).color;
			((ColorBlock)(ref colors)).highlightedColor = highlight;
			((ColorBlock)(ref colors)).pressedColor = pressed;
			((Selectable)obj).colors = colors;
			((UnityEvent)obj.onClick).AddListener(action);
		}

		private static void MakeSeparator(string name, Transform parent, float anchorY)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = MakeRect(name, parent, new Color(1f, 1f, 1f, 0.12f)).GetComponent<RectTransform>();
			component.anchorMin = new Vector2(0f, anchorY);
			component.anchorMax = new Vector2(1f, anchorY);
			component.sizeDelta = new Vector2(0f, 1f);
			component.anchoredPosition = Vector2.zero;
		}

		private static void Stretch(GameObject go, Vector2 min, Vector2 max)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = go.GetComponent<RectTransform>();
			component.anchorMin = min;
			component.anchorMax = max;
			Vector2 offsetMin = (component.offsetMax = Vector2.zero);
			component.offsetMin = offsetMin;
		}

		private static void SetAnchors(GameObject go, Vector2 min, Vector2 max, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = go.GetComponent<RectTransform>();
			component.anchorMin = min;
			component.anchorMax = max;
			component.offsetMin = offsetMin;
			component.offsetMax = offsetMax;
		}

		private static void SetPivotAnchor(GameObject go, Vector2 anchor, Vector2 size, Vector2 pos)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			RectTransform component = go.GetComponent<RectTransform>();
			Vector2 anchorMin = (component.anchorMax = anchor);
			component.anchorMin = anchorMin;
			component.sizeDelta = size;
			component.anchoredPosition = pos;
		}

		private static Font GetFont()
		{
			Font[] array = Resources.FindObjectsOfTypeAll<Font>();
			foreach (Font val in array)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name.Contains("Averia"))
				{
					return val;
				}
			}
			return Resources.GetBuiltinResource<Font>("Arial.ttf");
		}
	}
}
namespace ValheimPerformanceOverhaul.AI
{
	public class AIOptimizer : MonoBehaviour
	{
		private BaseAI _ai;

		private ZNetView _nview;

		private Character _character;

		private float _distCheckTimer;

		private const float DIST_CHECK_INTERVAL = 1f;

		private float _closestPlayerDistSqr = 1000000f;

		private Character _cachedEnemy;

		private float _lastEnemyCheck;

		private float _lastAttackCheck;

		private float _lastPathUpdate;

		private Vector3 _lastPathTargetPos;

		private readonly Dictionary<int, (bool visible, float time)> _losCache = new Dictionary<int, (bool, float)>();

		private const int MAX_LOS_CACHE_SIZE = 100;

		private const float LOS_CACHE_TIMEOUT = 5f;

		private float _losCleanupTimer;

		private Rigidbody _rigidbody;

		private Collider[] _colliders;

		private bool _physicsActive = true;

		private Animator _animator;

		private bool _animatorActive = true;

		private float _originalAnimatorSpeed = 1f;

		private void Awake()
		{
			_ai = ((Component)this).GetComponent<BaseAI>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			_character = ((Component)this).GetComponent<Character>();
			_rigidbody = ((Component)this).GetComponent<Rigidbody>();
			_colliders = ((Component)this).GetComponentsInChildren<Collider>();
			_animator = ((Component)this).GetComponentInChildren<Animator>();
			if ((Object)(object)_animator != (Object)null)
			{
				_originalAnimatorSpeed = _animator.speed;
			}
		}

		private void OnEnable()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			_closestPlayerDistSqr = 0f;
			_distCheckTimer = 1f;
			_cachedEnemy = null;
			_lastEnemyCheck = -100f;
			_lastAttackCheck = -100f;
			_lastPathUpdate = -100f;
			_lastPathTargetPos = Vector3.zero;
			_losCache.Clear();
			_losCleanupTimer = 0f;
			_physicsActive = true;
			_animatorActive = true;
			if ((Object)(object)_animator != (Object)null)
			{
				((Behaviour)_animator).enabled = true;
				_animator.speed = _originalAnimatorSpeed;
			}
			if ((Object)(object)_rigidbody != (Object)null && !_rigidbody.isKinematic)
			{
				_rigidbody.WakeUp();
			}
		}

		public float GetDistanceToPlayer()
		{
			return Mathf.Sqrt(_closestPlayerDistSqr);
		}

		private void FixedUpdate()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner())
			{
				return;
			}
			_distCheckTimer += Time.fixedDeltaTime;
			if (!(_distCheckTimer < 1f))
			{
				_distCheckTimer = 0f;
				UpdateClosestPlayerDistance();
				UpdatePhysicsAndAnimator();
				_losCleanupTimer += 1f;
				if (_losCleanupTimer >= 10f)
				{
					_losCleanupTimer = 0f;
					CleanupLOSCache();
				}
			}
		}

		private void UpdateClosestPlayerDistance()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			_closestPlayerDistSqr = 1000000f;
			Vector3 val;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position;
				_closestPlayerDistSqr = ((Vector3)(ref val)).sqrMagnitude;
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!((Object)(object)allPlayer == (Object)null))
				{
					val = ((Component)allPlayer).transform.position - ((Component)this).transform.position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < _closestPlayerDistSqr)
					{
						_closestPlayerDistSqr = sqrMagnitude;
					}
				}
			}
		}

		private void UpdatePhysicsAndAnimator()
		{
			if (!Plugin.AiThrottlingEnabled.Value)
			{
				return;
			}
			float distanceToPlayer = GetDistanceToPlayer();
			if (Plugin.CullPhysicsEnabled.Value && (Object)(object)_rigidbody != (Object)null)
			{
				bool flag = distanceToPlayer < 120f;
				if (flag != _physicsActive)
				{
					SetPhysicsActive(flag);
				}
			}
			if (!Plugin.AnimatorOptimizationEnabled.Value || !((Object)(object)_animator != (Object)null))
			{
				return;
			}
			if (distanceToPlayer > 120f)
			{
				if (_animatorActive)
				{
					((Behaviour)_animator).enabled = false;
					_animatorActive = false;
				}
			}
			else if (distanceToPlayer > 60f)
			{
				if (!_animatorActive)
				{
					((Behaviour)_animator).enabled = true;
					_animatorActive = true;
				}
				_animator.speed = _originalAnimatorSpeed * 0.5f;
			}
			else
			{
				if (!_animatorActive)
				{
					((Behaviour)_animator).enabled = true;
					_animatorActive = true;
				}
				_animator.speed = _originalAnimatorSpeed;
			}
		}

		private void SetPhysicsActive(bool active)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_character != (Object)null)
			{
				return;
			}
			if ((Object)(object)_rigidbody != (Object)null)
			{
				if (!active && !_rigidbody.isKinematic)
				{
					_rigidbody.velocity = Vector3.zero;
					_rigidbody.angularVelocity = Vector3.zero;
				}
				_rigidbody.isKinematic = !active;
			}
			if (_colliders != null)
			{
				Collider[] colliders = _colliders;
				foreach (Collider val in colliders)
				{
					if ((Object)(object)val != (Object)null)
					{
						val.enabled = active;
					}
				}
			}
			_physicsActive = active;
		}

		public bool ShouldCheckEnemy(out Character currentEnemy)
		{
			currentEnemy = _cachedEnemy;
			float time = Time.time;
			float interval = GetInterval(0.5f, 2f);
			if (time - _lastEnemyCheck >= interval)
			{
				_lastEnemyCheck = time;
				return true;
			}
			return false;
		}

		public void UpdateCachedEnemy(Character enemy)
		{
			_cachedEnemy = enemy;
		}

		public bool ShouldCheckAttack()
		{
			float time = Time.time;
			float interval = GetInterval(0.3f, 1f);
			if (time - _lastAttackCheck >= interval)
			{
				_lastAttackCheck = time;
				return true;
			}
			return false;
		}

		public bool ShouldUpdatePath(Vector3 targetPos)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			float time = Time.time;
			float interval = GetInterval(0.5f, 2f);
			bool num = time - _lastPathUpdate >= interval;
			Vector3 val = targetPos - _lastPathTargetPos;
			bool flag = ((Vector3)(ref val)).sqrMagnitude >= 4f;
			if (num || flag)
			{
				_lastPathUpdate = time;
				_lastPathTargetPos = targetPos;
				return true;
			}
			return false;
		}

		public bool GetCachedLOS(Character target, out bool visible)
		{
			visible = false;
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			int hashCode = ((object)target).GetHashCode();
			float time = Time.time;
			if (_losCache.TryGetValue(hashCode, out (bool, float) value) && time - value.Item2 < 0.5f)
			{
				(visible, _) = value;
				return true;
			}
			return false;
		}

		public void SetCachedLOS(Character target, bool visible)
		{
			if (!((Object)(object)target == (Object)null))
			{
				_losCache[((object)target).GetHashCode()] = (visible, Time.time);
			}
		}

		private float GetInterval(float min, float max)
		{
			if (_closestPlayerDistSqr < 1600f)
			{
				return min;
			}
			if (_closestPlayerDistSqr > 14400f)
			{
				return max;
			}
			float num = (_closestPlayerDistSqr - 1600f) / 12800f;
			return Mathf.Lerp(min, max, num);
		}

		private void CleanupLOSCache()
		{
			if (Time.frameCount % 600 != 0)
			{
				return;
			}
			float time = Time.time;
			List<int> list = new List<int>();
			foreach (KeyValuePair<int, (bool, float)> item in _losCache)
			{
				if (time - item.Value.Item2 > 10f)
				{
					list.Add(item.Key);
				}
			}
			foreach (int item2 in list)
			{
				_losCache.Remove(item2);
			}
		}

		private void OnDestroy()
		{
			_losCache.Clear();
		}
	}
	[HarmonyPatch]
	public static class AIPatches
	{
		private const float AI_ACTIVATION_RADIUS = 60f;

		private const float AI_DEACTIVATION_RADIUS = 80f;

		private const float AI_SLOW_UPDATE_INTERVAL = 5f;

		private static readonly Dictionary<BaseAI, float> _lastSlowUpdate = new Dictionary<BaseAI, float>();

		[HarmonyPatch(typeof(BaseAI), "Awake")]
		[HarmonyPostfix]
		private static void BaseAI_Awake_Postfix(BaseAI __instance)
		{
			if ((Object)(object)((Component)__instance).GetComponent<AIOptimizer>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<AIOptimizer>();
			}
			Character component = ((Component)__instance).GetComponent<Character>();
			if ((Object)(object)component != (Object)null && component.IsTamed() && (Object)(object)((Component)__instance).GetComponent<TamedIdleOptimizer>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<TamedIdleOptimizer>();
			}
			_lastSlowUpdate[__instance] = Time.time;
		}

		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		[HarmonyPrefix]
		private static bool MonsterAI_UpdateAI_Prefix(MonsterAI __instance, float dt)
		{
			if (!Plugin.AiThrottlingEnabled.Value)
			{
				return true;
			}
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				return true;
			}
			AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			float distanceToPlayer = component.GetDistanceToPlayer();
			if (distanceToPlayer <= 60f)
			{
				return true;
			}
			if (distanceToPlayer > 60f)
			{
				if (!_lastSlowUpdate.TryGetValue((BaseAI)(object)__instance, out var value))
				{
					_lastSlowUpdate[(BaseAI)(object)__instance] = Time.time;
					return false;
				}
				if (Time.time - value < 5f)
				{
					return false;
				}
				_lastSlowUpdate[(BaseAI)(object)__instance] = Time.time;
				if (Plugin.DebugLoggingEnabled.Value && Time.frameCount % 300 == 0)
				{
					Plugin.Log.LogInfo((object)$"[AI] Slow update for {((Object)__instance).name} at {distanceToPlayer:F1}m");
				}
				return true;
			}
			return true;
		}

		[HarmonyPatch(typeof(BaseAI), "OnDestroy")]
		[HarmonyPostfix]
		private static void BaseAI_OnDestroy_Postfix(BaseAI __instance)
		{
			_lastSlowUpdate.Remove(__instance);
		}

		[HarmonyPatch(typeof(BaseAI), "FindEnemy")]
		[HarmonyPrefix]
		private static bool FindEnemy_Prefix(BaseAI __instance, ref Character __result)
		{
			if (!Plugin.AiThrottlingEnabled.Value)
			{
				return true;
			}
			AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
			if ((Object)(object)component != (Object)null)
			{
				if (component.GetDistanceToPlayer() < 60f)
				{
					return true;
				}
				if (!component.ShouldCheckEnemy(out __result))
				{
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(BaseAI), "FindEnemy")]
		[HarmonyPostfix]
		private static void FindEnemy_Postfix(BaseAI __instance, Character __result)
		{
			if (Plugin.AiThrottlingEnabled.Value)
			{
				AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
				if ((Object)(object)component != (Object)null)
				{
					component.UpdateCachedEnemy(__result);
				}
			}
		}

		[HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Character) })]
		[HarmonyPrefix]
		private static bool CanSeeTarget_Prefix(BaseAI __instance, Character target, ref bool __result)
		{
			if (!Plugin.AiThrottlingEnabled.Value)
			{
				return true;
			}
			AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
			if ((Object)(object)component != (Object)null)
			{
				if (component.GetDistanceToPlayer() < 60f)
				{
					return true;
				}
				if (component.GetCachedLOS(target, out var visible))
				{
					__result = visible;
					return false;
				}
				if (component.GetDistanceToPlayer() > 40f)
				{
					__result = false;
					component.SetCachedLOS(target, visible: false);
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Character) })]
		[HarmonyPostfix]
		private static void CanSeeTarget_Postfix(BaseAI __instance, Character target, bool __result)
		{
			if (Plugin.AiThrottlingEnabled.Value)
			{
				AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
				if ((Object)(object)component != (Object)null && (Object)(object)target != (Object)null)
				{
					component.SetCachedLOS(target, __result);
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "DoAttack")]
		[HarmonyPrefix]
		private static bool DoAttack_Prefix(MonsterAI __instance)
		{
			if (!Plugin.AiThrottlingEnabled.Value)
			{
				return true;
			}
			AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>();
			if ((Object)(object)component != (Object)null)
			{
				if (component.GetDistanceToPlayer() < 60f)
				{
					return true;
				}
				return component.ShouldCheckAttack();
			}
			return true;
		}

		[HarmonyPatch(typeof(ZNet), "Update")]
		[HarmonyPostfix]
		private static void CleanupSlowUpdateCache()
		{
			if (!Plugin.AiThrottlingEnabled.Value || Time.frameCount % 3600 != 0)
			{
				return;
			}
			List<BaseAI> list = new List<BaseAI>();
			float time = Time.time;
			foreach (KeyValuePair<BaseAI, float> item in _lastSlowUpdate)
			{
				if ((Object)(object)item.Key == (Object)null || time - item.Value > 60f)
				{
					list.Add(item.Key);
				}
			}
			foreach (BaseAI item2 in list)
			{
				_lastSlowUpdate.Remove(item2);
			}
			if (Plugin.DebugLoggingEnabled.Value && list.Count > 0)
			{
				Plugin.Log.LogInfo((object)$"[AI] Cleaned {list.Count} stale slow update entries");
			}
		}
	}
	public class TamedIdleOptimizer : MonoBehaviour
	{
		private Character _character;

		private BaseAI _baseAI;

		private MonsterAI _monsterAI;

		private ZNetView _nview;

		private Animator _animator;

		private Rigidbody _rigidbody;

		private Collider[] _colliders;

		private bool _isInIdleMode;

		private float _lastStateChange;

		private float _lastCombatTime;

		private float _lastCheckTime;

		private bool _originalAIEnabled = true;

		private float _originalAnimatorSpeed = 1f;

		private bool _originalRigidbodyKinematic;

		private bool[] _originalColliderStates;

		private const float CHECK_INTERVAL = 1f;

		private const float MIN_TIME_IN_COMBAT = 5f;

		private const float STATE_CHANGE_COOLDOWN = 2f;

		private const float BASE_DETECTION_RADIUS = 30f;

		private void Awake()
		{
			_character = ((Component)this).GetComponent<Character>();
			_baseAI = ((Component)this).GetComponent<BaseAI>();
			_monsterAI = ((Component)this).GetComponent<MonsterAI>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			_animator = ((Component)this).GetComponentInChildren<Animator>();
			_rigidbody = ((Component)this).GetComponent<Rigidbody>();
			_colliders = ((Component)this).GetComponentsInChildren<Collider>();
			if ((Object)(object)_animator != (Object)null)
			{
				_originalAnimatorSpeed = _animator.speed;
			}
			if ((Object)(object)_rigidbody != (Object)null)
			{
				_originalRigidbodyKinematic = _rigidbody.isKinematic;
			}
			if (_colliders != null && _colliders.Length != 0)
			{
				_originalColliderStates = new bool[_colliders.Length];
				for (int i = 0; i < _colliders.Length; i++)
				{
					_originalColliderStates[i] = (Object)(object)_colliders[i] != (Object)null && _colliders[i].enabled;
				}
			}
			_lastCheckTime = Time.time;
		}

		private void Update()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return;
			}
			if (!Plugin.TamedIdleOptimizationEnabled.Value)
			{
				if (_isInIdleMode)
				{
					ExitIdleMode();
				}
				return;
			}
			float time = Time.time;
			if (time - _lastCheckTime < 1f)
			{
				return;
			}
			_lastCheckTime = time;
			if ((Object)(object)_character != (Object)null && _character.InAttack())
			{
				_lastCombatTime = time;
			}
			if (_isInIdleMode)
			{
				if (ShouldExitIdle())
				{
					ExitIdleMode();
				}
			}
			else if (ShouldEnterIdle())
			{
				EnterIdleMode();
			}
		}

		private bool ShouldEnterIdle()
		{
			if (Time.time - _lastStateChange < 2f)
			{
				return false;
			}
			if ((Object)(object)_baseAI != (Object)null && (Object)(object)_baseAI.GetTargetCreature() != (Object)null)
			{
				return false;
			}
			if (IsTamed() && !IsInCombat() && IsInsideBase() && !HasFollowTarget())
			{
				return Time.time - _lastCombatTime > Plugin.TamedIdleDistanceFromCombat.Value;
			}
			return false;
		}

		private bool ShouldExitIdle()
		{
			if (!IsInCombat() && !HasFollowTarget() && !PlayerInteracted())
			{
				return !IsInsideBase();
			}
			return true;
		}

		private bool IsTamed()
		{
			if ((Object)(object)_character == (Object)null)
			{
				return false;
			}
			return _character.IsTamed();
		}

		private bool IsInCombat()
		{
			if ((Object)(object)_character == (Object)null)
			{
				return false;
			}
			if (_character.InAttack())
			{
				return true;
			}
			_character.GetHealth();
			_character.GetMaxHealth();
			if ((Object)(object)_baseAI != (Object)null)
			{
				if (_baseAI.IsAlerted())
				{
					return true;
				}
				if ((Object)(object)_baseAI.GetTargetCreature() != (Object)null)
				{
					return true;
				}
				if (_baseAI.HaveTarget())
				{
					return true;
				}
			}
			if ((Object)(object)_monsterAI != (Object)null)
			{
				Character targetCreature = ((BaseAI)_monsterAI).GetTargetCreature();
				if ((Object)(object)targetCreature != (Object)null && !targetCreature.IsDead())
				{
					return true;
				}
			}
			return false;
		}

		private bool IsInsideBase()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false))
			{
				return true;
			}
			bool result = false;
			Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, Plugin.TamedIdleBaseDetectionRadius.Value);
			for (int i = 0; i < array.Length; i++)
			{
				if ((Object)(object)((Component)array[i]).GetComponentInParent<Piece>() != (Object)null)
				{
					result = true;
					break;
				}
			}
			return result;
		}

		private bool HasFollowTarget()
		{
			if ((Object)(object)_monsterAI == (Object)null)
			{
				return false;
			}
			return (Object)(object)_monsterAI.GetFollowTarget() != (Object)null;
		}

		private bool PlayerInteracted()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position;
				if (((Vector3)(ref val)).sqrMagnitude < 25f)
				{
					return true;
				}
			}
			return false;
		}

		private void EnterIdleMode()
		{
			if (_isInIdleMode)
			{
				return;
			}
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)("[TamedIdleOptimizer] " + ((Object)((Component)this).gameObject).name + " entering IDLE MODE"));
			}
			if ((Object)(object)_baseAI != (Object)null)
			{
				_originalAIEnabled = ((Behaviour)_baseAI).enabled;
				((Behaviour)_baseAI).enabled = false;
				_baseAI.StopMoving();
				if ((Object)(object)_monsterAI != (Object)null)
				{
					_monsterAI.SetFollowTarget((GameObject)null);
				}
			}
			if ((Object)(object)_animator != (Object)null)
			{
				_originalAnimatorSpeed = _animator.speed;
				_animator.speed = 0f;
				((Behaviour)_animator).enabled = false;
			}
			_isInIdleMode = true;
			_lastStateChange = Time.time;
		}

		private void ExitIdleMode()
		{
			if (_isInIdleMode)
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[TamedIdleOptimizer] " + ((Object)((Component)this).gameObject).name + " exiting IDLE MODE"));
				}
				if ((Object)(object)_baseAI != (Object)null)
				{
					((Behaviour)_baseAI).enabled = _originalAIEnabled;
				}
				if ((Object)(object)_animator != (Object)null)
				{
					((Behaviour)_animator).enabled = true;
					_animator.speed = _originalAnimatorSpeed;
				}
				_isInIdleMode = false;
				_lastStateChange = Time.time;
			}
		}

		public bool IsInIdleMode()
		{
			return _isInIdleMode;
		}

		private void OnDestroy()
		{
			if (_isInIdleMode)
			{
				ExitIdleMode();
			}
		}
	}
}
namespace ValheimPerformanceOverhaul.ObjectPooling
{
	[HarmonyPatch]
	public static class ObjectPoolingPatches
	{
		private static readonly FieldInfo _zdoField = AccessTools.Field(typeof(ZNetView), "m_zdo");

		private static readonly FieldInfo _instancesField = AccessTools.Field(typeof(ZNetScene), "m_instances");

		private static readonly HashSet<string> _poolingExclusions = new HashSet<string>
		{
			"spear_bronze", "spear_chitin", "spear_flint", "spear_carapace", "arrow_wood", "arrow_fire", "arrow_flint", "arrow_bronze", "arrow_iron", "arrow_silver",
			"arrow_poison", "arrow_frost", "arrow_needle", "arrow_obsidian", "arrow_carapace", "bomb", "tankard", "tankard_odin", "drop_stone", "drop_wood"
		};

		private static bool CanPoolObject(GameObject obj)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Invalid comparison between Unknown and I4
			if ((Object)(object)obj == (Object)null)
			{
				return false;
			}
			string text = ((Object)obj).name.Replace("(Clone)", "").ToLower();
			if (_poolingExclusions.Contains(text))
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ObjectPooling] Excluded from pooling: " + text));
				}
				return false;
			}
			ItemDrop component = obj.GetComponent<ItemDrop>();
			if ((Object)(object)component != (Object)null && component.m_itemData != null)
			{
				ItemData itemData = component.m_itemData;
				if (itemData.m_shared != null && (int)itemData.m_shared.m_itemType == 3 && itemData.m_shared.m_attack != null && (Object)(object)itemData.m_shared.m_attack.m_attackProjectile != (Object)null)
				{
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)("[ObjectPooling] Excluded projectile weapon: " + text));
					}
					return false;
				}
			}
			return true;
		}

		[HarmonyPatch(typeof(ZNetScene), "CreateObject")]
		[HarmonyPrefix]
		private static bool CreateObjectPrefix(ZNetScene __instance, ZDO zdo, ref GameObject __result)
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ObjectPoolingEnabled.Value)
			{
				return true;
			}
			GameObject prefab = __instance.GetPrefab(zdo.GetPrefab());
			if ((Object)(object)prefab == (Object)null)
			{
				return true;
			}
			if ((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null)
			{
				return true;
			}
			if (!CanPoolObject(prefab))
			{
				return true;
			}
			if (ObjectPoolManager.TryGetObject(((Object)prefab).name, out var instance))
			{
				if ((Object)(object)instance == (Object)null)
				{
					Plugin.Log.LogWarning((object)("[ObjectPooling] Pool returned null for " + ((Object)prefab).name + ", using original spawn"));
					return true;
				}
				if (instance.activeSelf)
				{
					Plugin.Log.LogError((object)("[ObjectPooling] Pool returned active object for " + ((Object)prefab).name + "! Using original spawn"));
					return true;
				}
				ZNetView component = instance.GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null)
				{
					ZDO zDO = component.GetZDO();
					if (zDO != null && zDO.IsValid())
					{
						Plugin.Log.LogError((object)("[ObjectPooling] Pool returned object with valid ZDO for " + ((Object)prefab).name + "! Using original spawn"));
						return true;
					}
				}
				try
				{
					instance.transform.position = zdo.GetPosition();
					instance.transform.rotation = zdo.GetRotation();
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("[ObjectPooling] Error setting transform: " + ex.Message));
					return true;
				}
				instance.SetActive(true);
				PooledObject pooledObject = instance.GetComponent<PooledObject>();
				if ((Object)(object)pooledObject == (Object)null)
				{
					pooledObject = instance.AddComponent<PooledObject>();
				}
				pooledObject.Initialize(((Object)prefab).name);
				if ((Object)(object)component != (Object)null)
				{
					try
					{
						ZDO zDO2 = component.GetZDO();
						if (zDO2 != null && zDO2.IsValid())
						{
							component.ResetZDO();
						}
						if (_zdoField != null)
						{
							_zdoField.SetValue(component, zdo);
						}
						if (_instancesField != null)
						{
							Dictionary<ZDO, ZNetView> dictionary = (Dictionary<ZDO, ZNetView>)_instancesField.GetValue(__instance);
							if (dictionary != null)
							{
								if (dictionary.ContainsKey(zdo))
								{
									Plugin.Log.LogError((object)("[ObjectPooling] ZDO already exists in instances! " + ((Object)prefab).name));
									instance.SetActive(false);
									ObjectPoolManager.ReturnObject(instance);
									return true;
								}
								dictionary[zdo] = component;
							}
						}
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogError((object)("[ObjectPooling] Error setting up ZNetView: " + ex2.Message));
						instance.SetActive(false);
						ObjectPoolManager.ReturnObject(instance);
						return true;
					}
				}
				__result = instance;
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ObjectPooling] Reused object: " + ((Object)prefab).name));
				}
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(ZNetScene), "Destroy")]
		[HarmonyPrefix]
		private static bool DestroyPrefix(ZNetScene __instance, GameObject go)
		{
			if (!Plugin.ObjectPoolingEnabled.Value || (Object)(object)go == (Object)null)
			{
				return true;
			}
			if ((Object)(object)go.GetComponent<ItemDrop>() == (Object)null)
			{
				return true;
			}
			if (!CanPoolObject(go))
			{
				return true;
			}
			ZNetView component = go.GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			try
			{
				if (_instancesField != null)
				{
					Dictionary<ZDO, ZNetView> dictionary = (Dictionary<ZDO, ZNetView>)_instancesField.GetValue(__instance);
					ZDO zDO = component.GetZDO();
					if (dictionary != null && zDO != null)
					{
						dictionary.Remove(zDO);
						if (zDO.IsOwner() && ZDOMan.instance != null)
						{
							ZDOMan.instance.DestroyZDO(zDO);
						}
					}
					if (component.GetZDO() != null)
					{
						component.ResetZDO();
					}
				}
				ObjectPoolManager.ReturnObject(go);
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ObjectPooling] Returned to pool: " + ((Object)go).name));
				}
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[ObjectPooling] Error returning to pool: " + ex.Message));
				return true;
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Update")]
		[HarmonyPostfix]
		private static void PerformPeriodicMaintenance()
		{
			if (Plugin.ObjectPoolingEnabled.Value)
			{
				if (Time.frameCount % 3600 == 0)
				{
					ObjectPoolManager.PerformMaintenance();
				}
				if (Plugin.DebugLoggingEnabled.Value && Time.frameCount % 18000 == 0)
				{
					ObjectPoolManager.LogPoolStats();
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		[HarmonyPostfix]
		private static void ClearPoolOnShutdown()
		{
			if (Plugin.ObjectPoolingEnabled.Value)
			{
				ObjectPoolManager.Clear();
			}
		}
	}
	public static class ObjectPoolManager
	{
		private static readonly Dictionary<int, Queue<GameObject>> _pools = new Dictionary<int, Queue<GameObject>>();

		private static readonly HashSet<GameObject> _objectsInUse = new HashSet<GameObject>();

		private static Transform _poolParent;

		private const int MAX_POOL_SIZE = 100;

		private static readonly object _lockObject = new object();

		private static readonly List<GameObject> _tempActiveCheck = new List<GameObject>(16);

		public static void Initialize()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (!((Object)(object)_poolParent != (Object)null))
			{
				GameObject val = new GameObject("_VPO_ObjectPool");
				_poolParent = val.transform;
				Object.DontDestroyOnLoad((Object)val);
				lock (_lockObject)
				{
					_pools.Clear();
					_objectsInUse.Clear();
				}
				Plugin.Log.LogInfo((object)"[ObjectPooling] Manager initialized.");
			}
		}

		public static bool TryGetObject(string prefabName, out GameObject instance)
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName);
			instance = null;
			lock (_lockObject)
			{
				if (_pools.TryGetValue(stableHashCode, out var value) && value.Count > 0)
				{
					int num = 0;
					while (value.Count > 0 && num < 5)
					{
						instance = value.Dequeue();
						num++;
						if ((Object)(object)instance == (Object)null)
						{
							Plugin.Log.LogWarning((object)("[ObjectPooling] Null object in pool for " + prefabName));
							continue;
						}
						if (instance.activeSelf)
						{
							Plugin.Log.LogError((object)("[ObjectPooling] Object already active: " + prefabName));
							_objectsInUse.Remove(instance);
							continue;
						}
						ZNetView component = instance.GetComponent<ZNetView>();
						if ((Object)(object)component != (Object)null)
						{
							ZDO zDO = component.GetZDO();
							if (zDO != null && zDO.IsValid())
							{
								Plugin.Log.LogError((object)("[ObjectPooling] Object has valid ZDO: " + prefabName));
								continue;
							}
						}
						if (_objectsInUse.Contains(instance))
						{
							Plugin.Log.LogError((object)("[ObjectPooling] Object already in use: " + prefabName));
							continue;
						}
						_objectsInUse.Add(instance);
						ResetObjectState(instance);
						return true;
					}
					instance = null;
					return false;
				}
			}
			return false;
		}

		public static void ReturnObject(GameObject instance)
		{
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			lock (_lockObject)
			{
				if (!_objectsInUse.Contains(instance))
				{
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)("[ObjectPooling] Object not from pool, destroying: " + ((Object)instance).name));
					}
					Object.Destroy((Object)(object)instance);
					return;
				}
				_objectsInUse.Remove(instance);
				CleanupObjectState(instance);
				instance.transform.SetParent((Transform)null);
				instance.SetActive(false);
				instance.transform.SetParent(_poolParent);
				PooledObject component = instance.GetComponent<PooledObject>();
				int key;
				if ((Object)(object)component != (Object)null)
				{
					key = component.PrefabHash;
					component.MarkAsAvailable();
				}
				else
				{
					key = StringExtensionMethods.GetStableHashCode(((Object)instance).name.Replace("(Clone)", ""));
				}
				if (!_pools.TryGetValue(key, out var value))
				{
					value = new Queue<GameObject>();
					_pools.Add(key, value);
				}
				if (value.Count < 100)
				{
					value.Enqueue(instance);
					return;
				}
				Object.Destroy((Object)(object)instance);
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogWarning((object)$"[ObjectPooling] Pool full ({100}), destroying: {((Object)instance).name}");
				}
			}
		}

		private static void CleanupObjectState(GameObject instance)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Rigidbody[] componentsInChildren = instance.GetComponentsInChildren<Rigidbody>(true);
				foreach (Rigidbody val in componentsInChildren)
				{
					if ((Object)(object)val != (Object)null && !val.isKinematic)
					{
						val.velocity = Vector3.zero;
						val.angularVelocity = Vector3.zero;
						val.Sleep();
					}
				}
				if ((Object)(object)instance.GetComponent<ItemDr