Decompiled source of ValheimPerformanceOverhaul v2.6.1
BepInEx/plugins/ValheimPerformanceOverhaul.dll
Decompiled 18 hours ago
The result has been truncated due to the large size, download it to view full contents!
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