Decompiled source of ValheimPerformanceOverhaul v2.6.1

BepInEx/plugins/ValheimPerformanceOverhaul.dll

Decompiled 18 hours 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.PostProcessing;
using ValheimPerformanceOverhaul.AI;
using ValheimPerformanceOverhaul.Audio;
using ValheimPerformanceOverhaul.ObjectPooling;

[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.6.0")]
	public class Plugin : BaseUnityPlugin
	{
		private const string PluginGUID = "com.Skarif.ValheimPerformanceOverhaul";

		private const string PluginName = "Valheim Performance Overhaul";

		private const string PluginVersion = "2.6.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;

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			Instance = this;
			SetupConfig();
			Log.LogInfo((object)"Initializing Valheim Performance Overhaul v2.6.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_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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
			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.");
			}
			GameObject val3 = new GameObject("_VPO_MemoryManager");
			val3.AddComponent<MemoryManager>();
			Object.DontDestroyOnLoad((Object)val3);
			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
			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>()));
		}

		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.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<ItemDrop>() != (Object)null)
				{
					Collider component = instance.GetComponent<Collider>();
					if ((Object)(object)component != (Object)null)
					{
						component.enabled = false;
					}
				}
				ParticleSystem[] componentsInChildren2 = instance.GetComponentsInChildren<ParticleSystem>(true);
				foreach (ParticleSystem val2 in componentsInChildren2)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						val2.Stop(true, (ParticleSystemStopBehavior)0);
					}
				}
				AudioSource[] componentsInChildren3 = instance.GetComponentsInChildren<AudioSource>(true);
				foreach (AudioSource val3 in componentsInChildren3)
				{
					if ((Object)(object)val3 != (Object)null)
					{
						val3.Stop();
						val3.clip = null;
					}
				}
				instance.transform.position = new Vector3(0f, -10000f, 0f);
				instance.transform.rotation = Quaternion.identity;
				instance.transform.localScale = Vector3.one;
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ObjectPooling] Cleaned up: " + ((Object)instance).name));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[ObjectPooling] Error cleaning up " + ((Object)instance).name + ": " + ex.Message));
			}
		}

		private static void ResetObjectState(GameObject instance)
		{
			try
			{
				Rigidbody[] componentsInChildren = instance.GetComponentsInChildren<Rigidbody>(true);
				foreach (Rigidbody val in componentsInChildren)
				{
					if ((Object)(object)val != (Object)null && !val.isKinematic)
					{
						val.WakeUp();
					}
				}
				Collider component = instance.GetComponent<Collider>();
				if ((Object)(object)component != (Object)null)
				{
					component.enabled = true;
				}
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ObjectPooling] Reset state: " + ((Object)instance).name));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[ObjectPooling] Error resetting " + ((Object)instance).name + ": " + ex.Message));
			}
		}

		public static void PerformMaintenance()
		{
			lock (_lockObject)
			{
				_tempActiveCheck.Clear();
				foreach (GameObject item in _objectsInUse)
				{
					if ((Object)(object)item == (Object)null || !item.activeSelf)
					{
						_tempActiveCheck.Add(item);
					}
				}
				foreach (GameObject item2 in _tempActiveCheck)
				{
					_objectsInUse.Remove(item2);
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogWarning((object)"[ObjectPooling] Cleaned dead reference from _objectsInUse");
					}
				}
				List<int> list = new List<int>();
				foreach (KeyValuePair<int, Queue<GameObject>> pool in _pools)
				{
					Queue<GameObject> value = pool.Value;
					Queue<GameObject> queue = new Queue<GameObject>();
					while (value.Count > 0)
					{
						GameObject val = value.Dequeue();
						if ((Object)(object)val != (Object)null)
						{
							queue.Enqueue(val);
						}
					}
					if (queue.Count == 0)
					{
						list.Add(pool.Key);
					}
					else
					{
						_pools[pool.Key] = queue;
					}
				}
				foreach (int item3 in list)
				{
					_pools.Remove(item3);
				}
				if (Plugin.DebugLoggingEnabled.Value && (_tempActiveCheck.Count > 0 || list.Count > 0))
				{
					Plugin.Log.LogInfo((object)$"[ObjectPooling] Maintenance: Removed {_tempActiveCheck.Count} dead references, {list.Count} empty pools");
				}
			}
		}

		public static void LogPoolStats()
		{
			if (!Plugin.DebugLoggingEnabled.Value)
			{
				return;
			}
			lock (_lockObject)
			{
				int num = 0;
				foreach (Queue<GameObject> value in _pools.Values)
				{
					num += value.Count;
				}
				Plugin.Log.LogInfo((object)$"[ObjectPooling] Stats - In use: {_objectsInUse.Count}, Pooled: {num}, Pool types: {_pools.Count}");
			}
		}

		public static void Clear()
		{
			lock (_lockObject)
			{
				foreach (Queue<GameObject> value in _pools.Values)
				{
					while (value.Count > 0)
					{
						GameObject val = value.Dequeue();
						if ((Object)(object)val != (Object)null)
						{
							Object.Destroy((Object)(object)val);
						}
					}
				}
				_pools.Clear();
				_objectsInUse.Clear();
				Plugin.Log.LogInfo((object)"[ObjectPooling] Cleared all pools");
			}
		}
	}
	public class PooledObject : MonoBehaviour
	{
		private bool _isInUse;

		private int _useCount;

		public int PrefabHash { get; private set; }

		public bool IsInUse => _isInUse;

		public int UseCount => _useCount;

		public void Initialize(string prefabName)
		{
			if (_isInUse)
			{
				Plugin.Log.LogError((object)$"[PooledObject] CRITICAL: Object already in use! {prefabName} (Use count: {_useCount})");
				MarkAsAvailable();
			}
			_isInUse = true;
			_useCount++;
			PrefabHash = StringExtensionMethods.GetStableHashCode(prefabName);
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"[PooledObject] Initialized: {prefabName} (Use #{_useCount})");
			}
		}

		public void MarkAsAvailable()
		{
			if (!_isInUse && _useCount > 0)
			{
				Plugin.Log.LogWarning((object)"[PooledObject] Object already available! Double return detected.");
			}
			_isInUse = false;
			if (Plugin.DebugLoggingEnabled.Value)
			{
				Plugin.Log.LogInfo((object)$"[PooledObject] Marked as available (Total uses: {_useCount})");
			}
		}

		private void OnDestroy()
		{
			if (_isInUse)
			{
				Plugin.Log.LogWarning((object)$"[PooledObject] Object destroyed while in use! Use count: {_useCount}");
			}
		}

		private void OnEnable()
		{
			if (!_isInUse && _useCount > 0)
			{
				Plugin.Log.LogError((object)"[PooledObject] CRITICAL: Object enabled but not marked as in use!");
			}
		}
	}
}
namespace ValheimPerformanceOverhaul.Vegetation
{
	[HarmonyPatch]
	public static class VegetationPatches
	{
		[HarmonyPatch(typeof(ClutterSystem), "Awake")]
		[HarmonyPostfix]
		private static void ReduceGrassDistance(ClutterSystem __instance)
		{
			if (!Plugin.VegetationOptimizationEnabled.Value)
			{
				return;
			}
			try
			{
				float value = Plugin.GrassRenderDistance.Value;
				FieldInfo fieldInfo = AccessTools.Field(typeof(ClutterSystem), "m_distance");
				if (fieldInfo != null)
				{
					fieldInfo.SetValue(__instance, value);
				}
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)$"[Vegetation] Set grass distance to {value}m");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[Vegetation] Failed to patch grass distance: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(ClutterSystem), "Awake")]
		[HarmonyPostfix]
		private static void ReduceGrassDensity(ClutterSystem __instance)
		{
			if (!Plugin.VegetationOptimizationEnabled.Value)
			{
				return;
			}
			try
			{
				float value = Plugin.GrassDensityMultiplier.Value;
				FieldInfo fieldInfo = AccessTools.Field(typeof(ClutterSystem), "m_clutter");
				if (fieldInfo != null && fieldInfo.GetValue(__instance) is IList list)
				{
					foreach (object item in list)
					{
						FieldInfo fieldInfo2 = AccessTools.Field(item.GetType(), "m_amount");
						if (fieldInfo2 != null)
						{
							int num = (int)fieldInfo2.GetValue(item);
							int num2 = Mathf.Max(1, Mathf.RoundToInt((float)num * value));
							fieldInfo2.SetValue(item, num2);
						}
					}
				}
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)$"[Vegetation] Set grass density to {value * 100f}%");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[Vegetation] Failed to patch grass density: " + ex.Message));
			}
		}

		[HarmonyPatch(typeof(Heightmap), "Awake")]
		[HarmonyPostfix]
		private static void OptimizeTerrainDetails(Heightmap __instance)
		{
			if (!Plugin.VegetationOptimizationEnabled.Value)
			{
				return;
			}
			try
			{
				Terrain component = ((Component)__instance).GetComponent<Terrain>();
				if ((Object)(object)component != (Object)null)
				{
					component.detailObjectDistance = Plugin.DetailObjectDistance.Value;
					component.detailObjectDensity = Plugin.DetailDensity.Value;
					component.heightmapMaximumLOD = Plugin.TerrainMaxLOD.Value;
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)$"[Vegetation] Terrain details optimized: distance={component.detailObjectDistance}, density={component.detailObjectDensity}");
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[Vegetation] Failed to optimize terrain: " + ex.Message));
			}
		}
	}
}
namespace ValheimPerformanceOverhaul.Particles
{
	public class TrackedParticle : MonoBehaviour
	{
		private bool _wasPlaying;

		private bool _isCulled;

		public ParticleSystem System { get; private set; }

		public bool IsCulled => _isCulled;

		private void Awake()
		{
			System = ((Component)this).GetComponent<ParticleSystem>();
		}

		public void SetCulled(bool culled)
		{
			if ((Object)(object)System == (Object)null || _isCulled == culled)
			{
				return;
			}
			if (culled)
			{
				_wasPlaying = System.isPlaying;
				if (_wasPlaying)
				{
					System.Stop(true, (ParticleSystemStopBehavior)1);
				}
			}
			else if (_wasPlaying && !System.isPlaying)
			{
				System.Play(true);
			}
			_isCulled = culled;
		}
	}
	public class ParticleSystemManager : MonoBehaviour
	{
		private readonly List<TrackedParticle> _allParticles = new List<TrackedParticle>(1024);

		private readonly HashSet<TrackedParticle> _culledParticles = new HashSet<TrackedParticle>();

		private float _updateTimer;

		private const float UPDATE_INTERVAL = 0.5f;

		private float _scanTimer;

		private const float SCAN_INTERVAL = 30f;

		private float _cullDistance = 50f;

		private int _maxActiveParticles = 30;

		private static Vector3 _cameraPos;

		private static readonly Comparison<TrackedParticle> _particleComparer = delegate(TrackedParticle a, TrackedParticle b)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)a).transform.position - _cameraPos;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			val = ((Component)b).transform.position - _cameraPos;
			float sqrMagnitude2 = ((Vector3)(ref val)).sqrMagnitude;
			return sqrMagnitude.CompareTo(sqrMagnitude2);
		};

		public static ParticleSystemManager 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);
		}

		private void Start()
		{
			LoadConfig();
			ScanForParticles();
			Plugin.Log.LogInfo((object)$"[ParticleOptimization] Initialized with {_allParticles.Count} particle systems.");
		}

		private void LoadConfig()
		{
			_cullDistance = Plugin.ParticleCullDistance?.Value ?? 50f;
			_maxActiveParticles = Plugin.MaxActiveParticles?.Value ?? 30;
		}

		private void ScanForParticles()
		{
			ParticleSystem[] array = Object.FindObjectsByType<ParticleSystem>((FindObjectsInactive)1, (FindObjectsSortMode)0);
			foreach (ParticleSystem ps in array)
			{
				RegisterParticleSystem(ps);
			}
		}

		public void RegisterParticleSystem(ParticleSystem ps)
		{
			if ((Object)(object)ps == (Object)null || (Object)(object)((Component)ps).GetComponent<TrackedParticle>() != (Object)null)
			{
				return;
			}
			string text = ((Object)((Component)ps).gameObject).name.ToLower();
			string text2 = ((Object)((Component)ps).transform.root).name.ToLower();
			if (text.Contains("ship") || text.Contains("vehicle") || text.Contains("boat") || text.Contains("karve") || text2.Contains("ship") || text2.Contains("vehicle") || text2.Contains("boat") || text2.Contains("karve"))
			{
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)("[ParticleOptimization] Excluded by name: " + ((Object)ps).name));
				}
			}
			else
			{
				if (((Component)ps).gameObject.layer == LayerMask.NameToLayer("UI"))
				{
					return;
				}
				if ((Object)(object)((Component)ps).GetComponentInParent<Ship>() != (Object)null)
				{
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)("[ParticleOptimization] Excluded ship particle: " + ((Object)ps).name));
					}
					return;
				}
				Rigidbody componentInParent = ((Component)ps).GetComponentInParent<Rigidbody>();
				if ((Object)(object)componentInParent != (Object)null && ((Object)((Component)componentInParent).gameObject).name.ToLower().Contains("vehicle"))
				{
					if (Plugin.DebugLoggingEnabled.Value)
					{
						Plugin.Log.LogInfo((object)("[ParticleOptimization] Excluded vehicle particle: " + ((Object)ps).name));
					}
				}
				else
				{
					TrackedParticle item = ((Component)ps).gameObject.AddComponent<TrackedParticle>();
					_allParticles.Add(item);
				}
			}
		}

		private void Update()
		{
			if (Plugin.ParticleOptimizationEnabled.Value && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				_updateTimer += Time.deltaTime;
				_scanTimer += Time.deltaTime;
				if (_scanTimer >= 30f)
				{
					_scanTimer = 0f;
					ScanForParticles();
				}
				if (!(_updateTimer < 0.5f))
				{
					_updateTimer = 0f;
					UpdateParticleCulling();
				}
			}
		}

		private void UpdateParticleCulling()
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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)
			for (int num = _allParticles.Count - 1; num >= 0; num--)
			{
				if ((Object)(object)_allParticles[num] == (Object)null || (Object)(object)_allParticles[num].System == (Object)null)
				{
					_allParticles.RemoveAt(num);
				}
			}
			if (_allParticles.Count == 0)
			{
				return;
			}
			_cameraPos = ((Component)Camera.main).transform.position;
			float num2 = _cullDistance * _cullDistance;
			_allParticles.Sort(_particleComparer);
			int num3 = 0;
			foreach (TrackedParticle allParticle in _allParticles)
			{
				if ((Object)(object)allParticle == (Object)null || (Object)(object)allParticle.System == (Object)null)
				{
					continue;
				}
				Vector3 val = ((Component)allParticle).transform.position - _cameraPos;
				if (((Vector3)(ref val)).sqrMagnitude <= num2 && num3 < _maxActiveParticles)
				{
					if (allParticle.IsCulled)
					{
						allParticle.SetCulled(culled: false);
						_culledParticles.Remove(allParticle);
					}
					num3++;
				}
				else if (!allParticle.IsCulled)
				{
					allParticle.SetCulled(culled: true);
					_culledParticles.Add(allParticle);
				}
			}
			if (Plugin.DebugLoggingEnabled.Value && Time.frameCount % 240 == 0)
			{
				Plugin.Log.LogInfo((object)$"[ParticleOptimization] Active: {num3}/{_maxActiveParticles}, Culled: {_culledParticles.Count}, Total: {_allParticles.Count}");
			}
		}

		private void OnDestroy()
		{
			foreach (TrackedParticle culledParticle in _culledParticles)
			{
				if ((Object)(object)culledParticle != (Object)null)
				{
					culledParticle.SetCulled(culled: false);
				}
			}
			Instance = null;
		}
	}
	[HarmonyPatch]
	public static class ParticlePatches
	{
		[HarmonyPatch(typeof(Player), "OnSpawned")]
		[HarmonyPostfix]
		private static void InitializeParticleManager(Player __instance)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			if (Plugin.ParticleOptimizationEnabled.Value && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)ParticleSystemManager.Instance != (Object)null))
			{
				GameObject val = new GameObject("_VPO_ParticleSystemManager");
				Object.DontDestroyOnLoad((Object)val);
				val.AddComponent<ParticleSystemManager>();
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "CreateObject")]
		[HarmonyPostfix]
		private static void RegisterNewParticles(GameObject __result)
		{
			if (Plugin.ParticleOptimizationEnabled.Value && !((Object)(object)ParticleSystemManager.Instance == (Object)null) && !((Object)(object)__result == (Object)null))
			{
				ParticleSystem[] componentsInChildren = __result.GetComponentsInChildren<ParticleSystem>(true);
				foreach (ParticleSystem ps in componentsInChildren)
				{
					ParticleSystemManager.Instance.RegisterParticleSystem(ps);
				}
			}
		}
	}
}
namespace ValheimPerformanceOverhaul.Audio
{
	[HarmonyPatch]
	public static class AudioPatches
	{
		[HarmonyPatch(typeof(ZSFX), "Play")]
		[HarmonyPrefix]
		private static bool ZSFX_Play_Prefix(ZSFX __instance)
		{
			if (!Plugin.AudioPoolingEnabled.Value)
			{
				return true;
			}
			AudioSource component = ((Component)__instance).GetComponent<AudioSource>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			if (AudioPoolManager.TryPlayClip(component))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(AudioSource), "Play", new Type[] { })]
		[HarmonyPrefix]
		private static bool AudioSource_Play_Prefix(AudioSource __instance)
		{
			if (!Plugin.AudioPoolingEnabled.Value)
			{
				return true;
			}
			if ((Object)(object)((Component)__instance).GetComponent<ZSFX>() != (Object)null)
			{
				return true;
			}
			if ((Object)(object)((Component)__instance).GetComponent<PooledAudio>() != (Object)null)
			{
				return true;
			}
			if (__instance.spatialBlend < 0.1f)
			{
				return true;
			}
			if ((Object)(object)__instance.outputAudioMixerGroup != (Object)null)
			{
				string text = ((Object)__instance.outputAudioMixerGroup).name.ToLower();
				if (text.Contains("music") || text.Contains("gui"))
				{
					return true;
				}
			}
			if (__instance.loop)
			{
				return true;
			}
			if (AudioPoolManager.TryPlayClip(__instance))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(AudioSource), "PlayOneShot", new Type[]
		{
			typeof(AudioClip),
			typeof(float)
		})]
		[HarmonyPrefix]
		private static bool AudioSource_PlayOneShot_Prefix(AudioSource __instance, AudioClip clip, float volumeScale)
		{
			_ = Plugin.AudioPoolingEnabled.Value;
			return true;
		}
	}
	public class PooledAudio : MonoBehaviour
	{
		private bool _isAvailable = true;

		private float _playStartTime;

		private float _clipLength;

		public AudioSource Source { get; private set; }

		public bool IsAvailable => _isAvailable;

		private void Awake()
		{
			Source = ((Component)this).GetComponent<AudioSource>();
		}

		private void Update()
		{
			if (!_isAvailable && (Object)(object)Source != (Object)null)
			{
				float num = _playStartTime + _clipLength + 0.1f;
				if (Time.time >= num && !Source.isPlaying)
				{
					SetAvailable(available: true);
				}
			}
		}

		public void Play(AudioSource original)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Source == (Object)null) && !((Object)(object)original == (Object)null) && !((Object)(object)original.clip == (Object)null))
			{
				_isAvailable = false;
				_playStartTime = Time.time;
				_clipLength = original.clip.length;
				if (original.pitch > 0.01f)
				{
					_clipLength /= Mathf.Abs(original.pitch);
				}
				Source.clip = original.clip;
				Source.volume = original.volume;
				Source.pitch = original.pitch;
				Source.loop = original.loop;
				Source.outputAudioMixerGroup = original.outputAudioMixerGroup;
				Source.spatialBlend = original.spatialBlend;
				Source.rolloffMode = original.rolloffMode;
				Source.minDistance = original.minDistance;
				Source.maxDistance = original.maxDistance;
				Source.spread = original.spread;
				Source.dopplerLevel = original.dopplerLevel;
				Source.priority = original.priority;
				((Component)Source).transform.position = ((Component)original).transform.position;
				Source.Play();
				if (Plugin.DebugLoggingEnabled.Value)
				{
					Plugin.Log.LogInfo((object)$"[AudioPooling] Playing clip: {((Object)original.clip).name}, length: {_clipLength:F2}s, pitch: {original.pitch:F2}");
				}
			}
		}

		public void SetAvailable(bool available)
		{
			if (_isAvailable == available)
			{
				return;
			}
			_isAvailable = available;
			if (available)
			{
				if ((Object)(object)Source != (Object)null)
				{
					Source.Stop();
					Source.clip = null;
				}
				AudioPoolManager.ReturnToPool(this);
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Source != (Object)null)
			{
				Source.Stop();
			}
		}
	}
	public static class AudioPoolManager
	{
		private static readonly Stack<PooledAudio> _availablePool = new Stack<PooledAudio>();

		private static rea