Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimPerformanceOverhaul v2.7.2
BepInEx/plugins/ValheimPerformanceOverhaul.dll
Decompiled a week 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.Events; using UnityEngine.PostProcessing; using UnityEngine.UI; using ValheimPerformanceOverhaul.AI; using ValheimPerformanceOverhaul.Audio; using ValheimPerformanceOverhaul.Core; using ValheimPerformanceOverhaul.ObjectPooling; using ValheimPerformanceOverhaul.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ValheimPerformanceOverhaul")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ValheimPerformanceOverhaul")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e1d59e72-fa59-450d-94cd-86cc02deca70")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace ValheimPerformanceOverhaul { [HarmonyPatch(typeof(Resources), "UnloadUnusedAssets")] public static class GCPatches { private delegate bool InAttackDelegate(Player player); private static FieldRef<Character, Vector3> _getMoveDirFast; private static InAttackDelegate _inAttackFast; private static bool _delegatesInitialized; static GCPatches() { try { FieldInfo fieldInfo = AccessTools.Field(typeof(Character), "m_moveDir"); MethodInfo methodInfo = AccessTools.Method(typeof(Player), "InAttack", (Type[])null, (Type[])null); if (fieldInfo != null && methodInfo != null) { _getMoveDirFast = AccessTools.FieldRefAccess<Character, Vector3>(fieldInfo); _inAttackFast = (InAttackDelegate)Delegate.CreateDelegate(typeof(InAttackDelegate), methodInfo); _delegatesInitialized = true; Plugin.Log.LogInfo((object)"[GC] Fast delegates initialized successfully."); } else { Plugin.Log.LogWarning((object)"[GC] Could not find private fields. GC optimization disabled."); } } catch (Exception ex) { Plugin.Log.LogError((object)("[GC] Failed to initialize delegates: " + ex.Message)); _delegatesInitialized = false; } } [HarmonyPrefix] private static bool PreventGCWhenPlayerIsBusy() { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.GcControlEnabled.Value || !_delegatesInitialized) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead()) { return true; } try { if (!((Character)localPlayer).IsOnGround() || ((Character)localPlayer).IsSwimming() || ((Character)localPlayer).IsTeleporting()) { return false; } Vector3 val = _getMoveDirFast.Invoke((Character)(object)localPlayer); if (((Vector3)(ref val)).sqrMagnitude > 0.01f) { return false; } if (_inAttackFast(localPlayer)) { return false; } } catch (Exception ex) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogWarning((object)("[GC] Error checking player state: " + ex.Message)); } return true; } return true; } } [HarmonyPatch] public static class JitPatches { [HarmonyPatch(typeof(Player), "OnSpawned")] [HarmonyPostfix] private static void WarmupGameMethods(Player __instance) { if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Plugin.JitWarmupEnabled.Value) { return; } try { Prepare(AccessTools.Method(typeof(Player), "GetHoverObject", (Type[])null, (Type[])null), "Player.GetHoverObject"); Prepare(AccessTools.Method(typeof(Player), "GetHoverCreature", (Type[])null, (Type[])null), "Player.GetHoverCreature"); Prepare(AccessTools.Method(typeof(Player), "GetHoveringPiece", (Type[])null, (Type[])null), "Player.GetHoveringPiece"); Prepare(AccessTools.Method(typeof(EnvMan), "GetCurrentBiome", (Type[])null, (Type[])null), "EnvMan.GetCurrentBiome"); Prepare(AccessTools.Method(typeof(InventoryGui), "Show", (Type[])null, (Type[])null), "InventoryGui.Show"); Prepare(AccessTools.Method(typeof(Character), "Damage", (Type[])null, (Type[])null), "Character.Damage"); } catch (Exception arg) { Plugin.Log.LogError((object)$"JIT Warm-up error: {arg}"); } } private static void Prepare(MethodInfo method, string methodName) { if (method != null) { RuntimeHelpers.PrepareMethod(method.MethodHandle); } } } [BepInPlugin("com.Skarif.ValheimPerformanceOverhaul", "Valheim Performance Overhaul", "2.7.0")] public class Plugin : BaseUnityPlugin { private const string PluginGUID = "com.Skarif.ValheimPerformanceOverhaul"; private const string PluginName = "Valheim Performance Overhaul"; public const string PluginVersion = "2.7.0"; private readonly Harmony _harmony = new Harmony("com.Skarif.ValheimPerformanceOverhaul"); public static ManualLogSource Log; public static Plugin Instance; public static ConfigEntry<bool> DebugLoggingEnabled; public static ConfigEntry<bool> GcControlEnabled; public static ConfigEntry<bool> DistanceCullerEnabled; public static ConfigEntry<float> CreatureCullDistance; public static ConfigEntry<float> PieceCullDistance; public static ConfigEntry<bool> CullPhysicsEnabled; public static ConfigEntry<bool> AiThrottlingEnabled; public static ConfigEntry<string> CullerExclusions; public static ConfigEntry<bool> ObjectPoolingEnabled; public static ConfigEntry<bool> JitWarmupEnabled; public static ConfigEntry<bool> LightCullingEnabled; public static ConfigEntry<int> MaxActiveLights; public static ConfigEntry<float> LightCullDistance; public static ConfigEntry<int> MaxShadowCasters; public static ConfigEntry<float> ShadowCullDistance; public static ConfigEntry<bool> LightLODEnabled; public static ConfigEntry<float> LightLODFullDistance; public static ConfigEntry<float> LightLODNoShadowDistance; public static ConfigEntry<float> LightLODEmissiveDistance; public static ConfigEntry<float> LightLODBillboardDistance; public static ConfigEntry<bool> AudioPoolingEnabled; public static ConfigEntry<int> AudioPoolSize; public static ConfigEntry<bool> GraphicsSettingsEnabled; public static ConfigEntry<float> ConfigShadowDistance; public static ConfigEntry<int> ConfigShadowResolution; public static ConfigEntry<int> ConfigShadowCascades; public static ConfigEntry<float> ConfigTerrainQuality; public static ConfigEntry<bool> ConfigReflections; public static ConfigEntry<bool> ConfigBloom; public static ConfigEntry<bool> PieceOptimizationEnabled; public static ConfigEntry<float> PieceUpdateInterval; public static ConfigEntry<float> PieceColliderDistance; public static ConfigEntry<float> PieceSupportCacheDuration; public static ConfigEntry<float> PieceUpdateSkipDistance; public static ConfigEntry<bool> ParticleOptimizationEnabled; public static ConfigEntry<float> ParticleCullDistance; public static ConfigEntry<int> MaxActiveParticles; public static ConfigEntry<bool> VegetationOptimizationEnabled; public static ConfigEntry<float> GrassRenderDistance; public static ConfigEntry<float> GrassDensityMultiplier; public static ConfigEntry<float> DetailObjectDistance; public static ConfigEntry<float> DetailDensity; public static ConfigEntry<int> TerrainMaxLOD; public static ConfigEntry<bool> AnimatorOptimizationEnabled; public static ConfigEntry<bool> MinimapOptimizationEnabled; public static ConfigEntry<int> MinimapTextureSize; public static ConfigEntry<int> MinimapUpdateInterval; public static ConfigEntry<bool> TamedIdleOptimizationEnabled; public static ConfigEntry<float> TamedIdleDistanceFromCombat; public static ConfigEntry<float> TamedIdleBaseDetectionRadius; public static ConfigEntry<float> TamedIdleCheckInterval; public static ConfigEntry<bool> LightFlickerOptimizationEnabled; public static ConfigEntry<bool> SmokeOptimizationEnabled; public static ConfigEntry<float> SmokeLiftForce; public static ConfigEntry<bool> EngineQualitySettingsEnabled; public static ConfigEntry<int> ParticleRaycastBudget; public static ConfigEntry<bool> SkipIntroEnabled; public static ConfigEntry<bool> FrameBudgetGuardEnabled; public static ConfigEntry<float> FrameBudgetThresholdMs; public static ConfigEntry<float> FrameBudgetThrottledDelta; public static ConfigEntry<float> FrameBudgetNormalDelta; private void Awake() { _harmony.PatchAll(typeof(WelcomeMessage)); Log = ((BaseUnityPlugin)this).Logger; Instance = this; SetupConfig(); Log.LogInfo((object)"Initializing Valheim Performance Overhaul v2.7.0..."); if (GraphicsSettingsEnabled.Value) { ApplyImmediateGraphicsSettings(); } Log.LogInfo((object)"Applying Harmony patches..."); try { _harmony.PatchAll(Assembly.GetExecutingAssembly()); Log.LogInfo((object)"All patches applied successfully."); } catch (Exception ex) { Log.LogError((object)("Error applying patches: " + ex.Message + "\n" + ex.StackTrace)); } } private void Start() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown if (ObjectPoolingEnabled.Value) { ObjectPoolManager.Initialize(); Log.LogInfo((object)"[ObjectPooling] System initialized."); } if (AudioPoolingEnabled.Value) { AudioPoolManager.Initialize(); Log.LogInfo((object)"[AudioPooling] System initialized."); } if (DistanceCullerEnabled.Value) { GameObject val = new GameObject("_VPO_DistanceCullerManager"); val.AddComponent<DistanceCullerManager>(); Object.DontDestroyOnLoad((Object)val); Log.LogInfo((object)"[DistanceCuller] Manager initialized."); } if (AiThrottlingEnabled.Value) { GameObject val2 = new GameObject("_VPO_AIOptimizer"); val2.AddComponent<AIOptimizer>(); Object.DontDestroyOnLoad((Object)val2); Log.LogInfo((object)"[AI] Optimizer manager initialized."); } if (FrameBudgetGuardEnabled.Value) { GameObject val3 = new GameObject("_VPO_FrameBudgetGuard"); val3.AddComponent<FrameBudgetGuard>(); Object.DontDestroyOnLoad((Object)val3); Log.LogInfo((object)"[FrameBudgetGuard] Initialized."); } GameObject val4 = new GameObject("_VPO_MemoryManager"); val4.AddComponent<MemoryManager>(); Object.DontDestroyOnLoad((Object)val4); Log.LogInfo((object)"[MemoryManager] Initialized."); } private void SetupConfig() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02d5: Expected O, but got Unknown //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Expected O, but got Unknown //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Expected O, but got Unknown //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Expected O, but got Unknown //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Expected O, but got Unknown //IL_0436: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Expected O, but got Unknown //IL_047a: Unknown result type (might be due to invalid IL or missing references) //IL_0484: Expected O, but got Unknown //IL_04ab: Unknown result type (might be due to invalid IL or missing references) //IL_04b5: Expected O, but got Unknown //IL_04e8: Unknown result type (might be due to invalid IL or missing references) //IL_04f2: Expected O, but got Unknown //IL_0585: Unknown result type (might be due to invalid IL or missing references) //IL_058f: Expected O, but got Unknown //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Expected O, but got Unknown //IL_05ff: Unknown result type (might be due to invalid IL or missing references) //IL_0609: Expected O, but got Unknown //IL_063c: Unknown result type (might be due to invalid IL or missing references) //IL_0646: Expected O, but got Unknown //IL_0699: Unknown result type (might be due to invalid IL or missing references) //IL_06a3: Expected O, but got Unknown //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d7: Expected O, but got Unknown //IL_072a: Unknown result type (might be due to invalid IL or missing references) //IL_0734: Expected O, but got Unknown //IL_0767: Unknown result type (might be due to invalid IL or missing references) //IL_0771: Expected O, but got Unknown //IL_07a4: Unknown result type (might be due to invalid IL or missing references) //IL_07ae: Expected O, but got Unknown //IL_07e1: Unknown result type (might be due to invalid IL or missing references) //IL_07eb: Expected O, but got Unknown //IL_0812: Unknown result type (might be due to invalid IL or missing references) //IL_081c: Expected O, but got Unknown //IL_0896: Unknown result type (might be due to invalid IL or missing references) //IL_08a0: Expected O, but got Unknown //IL_08c8: Unknown result type (might be due to invalid IL or missing references) //IL_08d2: Expected O, but got Unknown //IL_0925: Unknown result type (might be due to invalid IL or missing references) //IL_092f: Expected O, but got Unknown //IL_0962: Unknown result type (might be due to invalid IL or missing references) //IL_096c: Expected O, but got Unknown //IL_099f: Unknown result type (might be due to invalid IL or missing references) //IL_09a9: Expected O, but got Unknown //IL_0a1c: Unknown result type (might be due to invalid IL or missing references) //IL_0a26: Expected O, but got Unknown //IL_0a76: Unknown result type (might be due to invalid IL or missing references) //IL_0a80: Expected O, but got Unknown //IL_0af3: Unknown result type (might be due to invalid IL or missing references) //IL_0afd: Expected O, but got Unknown //IL_0b30: Unknown result type (might be due to invalid IL or missing references) //IL_0b3a: Expected O, but got Unknown //IL_0b6d: Unknown result type (might be due to invalid IL or missing references) //IL_0b77: Expected O, but got Unknown DebugLoggingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1. General", "Enable Debug Logging", false, "Enables detailed diagnostic logs for troubleshooting."); GcControlEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("2. GC Control", "Enabled", true, "Prevents garbage collection during combat or movement to reduce stuttering."); DistanceCullerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enabled", true, "Disables or throttles logic for distant objects to improve performance."); CreatureCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3. Distance Culler", "Creature Cull Distance", 80f, new ConfigDescription("Distance at which creatures are put to sleep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 200f), Array.Empty<object>())); PieceCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("3. Distance Culler", "Piece Cull Distance", 100f, new ConfigDescription("Distance at which build pieces are put to sleep.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 300f), Array.Empty<object>())); CullPhysicsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enable Physics Culling", true, "Disables Rigidbody physics on distant objects."); AiThrottlingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3. Distance Culler", "Enable AI Throttling", true, "Reduces how often distant AI updates to save CPU cycles."); CullerExclusions = ((BaseUnityPlugin)this).Config.Bind<string>("3. Distance Culler", "Exclusions", "TombStone,portal_wood", "Comma-separated list of prefab names to NEVER cull."); ObjectPoolingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("4. Object Pooling", "Enabled", true, "Reuses ItemDrop objects to reduce instantiation overhead."); JitWarmupEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5. JIT Warm-up", "Enabled", true, "Pre-compiles critical methods at game start to prevent initial stuttering."); LightCullingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Light Culling", "Enabled", true, "Disables distant light sources to improve performance. MAJOR FPS improvement!"); MaxActiveLights = ((BaseUnityPlugin)this).Config.Bind<int>("6. Light Culling", "Max Active Lights", 15, new ConfigDescription("Maximum number of active lights near the player.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 50), Array.Empty<object>())); LightCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "Light Cull Distance", 60f, new ConfigDescription("Maximum distance for active lights.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>())); MaxShadowCasters = ((BaseUnityPlugin)this).Config.Bind<int>("6. Light Culling", "Max Shadow Casters", 5, new ConfigDescription("Maximum number of lights that can cast shadows simultaneously.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 15), Array.Empty<object>())); ShadowCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "Shadow Cull Distance", 30f, new ConfigDescription("Distance at which shadows are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 80f), Array.Empty<object>())); LightLODEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("6. Light Culling", "Enable Light LOD System", true, "Enables Level of Detail system for lights. MASSIVE FPS improvement!"); LightLODFullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Full Light Distance", 20f, new ConfigDescription("Distance for full quality light with shadows.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 50f), Array.Empty<object>())); LightLODNoShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD No Shadow Distance", 40f, new ConfigDescription("Distance at which shadows are removed but light remains.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 80f), Array.Empty<object>())); LightLODEmissiveDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Emissive Distance", 70f, new ConfigDescription("Distance at which light is replaced with emissive material.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 120f), Array.Empty<object>())); LightLODBillboardDistance = ((BaseUnityPlugin)this).Config.Bind<float>("6. Light Culling", "LOD Billboard Distance", 100f, new ConfigDescription("Distance at which emissive is replaced with simple billboard.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 200f), Array.Empty<object>())); AudioPoolingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("7. Audio Optimization", "Enabled", true, "Reuses sound effect objects to reduce allocations."); AudioPoolSize = ((BaseUnityPlugin)this).Config.Bind<int>("7. Audio Optimization", "Total Pool Size", 32, new ConfigDescription("Total number of reusable AudioSource components.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(16, 128), Array.Empty<object>())); GraphicsSettingsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enabled", true, "Enables advanced graphics settings module."); ConfigShadowDistance = ((BaseUnityPlugin)this).Config.Bind<float>("8. Graphics Settings", "Shadow Distance", 50f, new ConfigDescription("Maximum distance for shadow rendering.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 150f), Array.Empty<object>())); ConfigShadowResolution = ((BaseUnityPlugin)this).Config.Bind<int>("8. Graphics Settings", "Shadow Resolution", 512, new ConfigDescription("Shadow resolution quality.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 512, 1024, 2048, 4096 }), Array.Empty<object>())); ConfigShadowCascades = ((BaseUnityPlugin)this).Config.Bind<int>("8. Graphics Settings", "Shadow Cascades", 1, new ConfigDescription("Number of shadow cascades.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 4), Array.Empty<object>())); ConfigTerrainQuality = ((BaseUnityPlugin)this).Config.Bind<float>("8. Graphics Settings", "Terrain Quality Multiplier", 0.7f, new ConfigDescription("Terrain detail quality.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>())); ConfigReflections = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enable Reflections", false, "Enables screen-space reflections."); ConfigBloom = ((BaseUnityPlugin)this).Config.Bind<bool>("8. Graphics Settings", "Enable Bloom", false, "Enables bloom glow effect."); PieceOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("10. Piece Optimization", "Enabled", true, "Optimizes building piece updates and collision detection."); PieceUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Update Interval (seconds)", 2f, new ConfigDescription("How often building pieces update their state.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 10f), Array.Empty<object>())); PieceColliderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Collider Disable Distance", 80f, new ConfigDescription("Distance at which building colliders are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 200f), Array.Empty<object>())); PieceSupportCacheDuration = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Support Cache Duration (seconds)", 5f, new ConfigDescription("How long to cache building support calculations.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); PieceUpdateSkipDistance = ((BaseUnityPlugin)this).Config.Bind<float>("10. Piece Optimization", "Update Skip Distance", 50f, new ConfigDescription("Distance at which building pieces stop updating entirely.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 150f), Array.Empty<object>())); ParticleOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("11. Particle Optimization", "Enabled", true, "Optimizes particle systems (fire, smoke, sparks)."); ParticleCullDistance = ((BaseUnityPlugin)this).Config.Bind<float>("11. Particle Optimization", "Particle Cull Distance", 50f, new ConfigDescription("Distance at which particle systems are disabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 100f), Array.Empty<object>())); MaxActiveParticles = ((BaseUnityPlugin)this).Config.Bind<int>("11. Particle Optimization", "Max Active Particle Systems", 30, new ConfigDescription("Maximum number of active particle systems.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 100), Array.Empty<object>())); VegetationOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("12. Vegetation Optimization", "Enabled", true, "Optimizes grass, bushes and terrain details."); GrassRenderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Grass Render Distance", 60f, new ConfigDescription("Distance at which grass is rendered.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(30f, 120f), Array.Empty<object>())); GrassDensityMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Grass Density Multiplier", 0.7f, new ConfigDescription("Grass density. 1.0 = vanilla, 0.5 = half grass.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1f), Array.Empty<object>())); DetailObjectDistance = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Detail Object Distance", 80f, new ConfigDescription("Distance for small objects (stones, sticks).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 150f), Array.Empty<object>())); DetailDensity = ((BaseUnityPlugin)this).Config.Bind<float>("12. Vegetation Optimization", "Detail Density", 0.7f, new ConfigDescription("Density of detail objects.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 1f), Array.Empty<object>())); TerrainMaxLOD = ((BaseUnityPlugin)this).Config.Bind<int>("12. Vegetation Optimization", "Terrain Max LOD", 1, new ConfigDescription("Maximum LOD level for terrain.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>())); AnimatorOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("13. Animator Optimization", "Enabled", true, "Optimizes character animations."); MinimapOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("15. Minimap Optimization", "Enabled", true, "Reduces minimap texture size and update frequency."); MinimapTextureSize = ((BaseUnityPlugin)this).Config.Bind<int>("15. Minimap Optimization", "Minimap Texture Size", 1024, new ConfigDescription("Minimap texture resolution.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[3] { 512, 1024, 2048 }), Array.Empty<object>())); MinimapUpdateInterval = ((BaseUnityPlugin)this).Config.Bind<int>("15. Minimap Optimization", "Update Interval (frames)", 2, new ConfigDescription("Update minimap every N frames.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>())); TamedIdleOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("16. Tamed Mob Idle Optimization", "Enabled", true, "Enables BASE IDLE MODE for tamed mobs on base. Dramatically reduces CPU load from idle tamed creatures."); TamedIdleDistanceFromCombat = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Idle Distance From Combat (seconds)", 5f, new ConfigDescription("Time after combat before mob can enter idle mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(3f, 30f), Array.Empty<object>())); TamedIdleBaseDetectionRadius = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Base Detection Radius", 30f, new ConfigDescription("Radius to detect player structures (base detection).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 60f), Array.Empty<object>())); TamedIdleCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("16. Tamed Mob Idle Optimization", "Check Interval (seconds)", 1f, new ConfigDescription("How often to check if mob should enter/exit idle mode.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 5f), Array.Empty<object>())); LightFlickerOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("17. Light Flicker Optimization", "Enabled", true, "Fixes light intensity to base value, eliminating Shadow Map recalculation every frame from fires and torches. RECOMMENDED: Large performance gain on bases with many light sources."); SmokeOptimizationEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("18. Smoke Physics Optimization", "Enabled", true, "Replaces complex smoke aerodynamics with simple linear interpolation. Dramatic CPU savings on large bases with many campfires."); SmokeLiftForce = ((BaseUnityPlugin)this).Config.Bind<float>("18. Smoke Physics Optimization", "Smoke Lift Force", 3.5f, new ConfigDescription("Upward force applied to smoke particles with simplified physics. Higher = faster rising smoke.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>())); EngineQualitySettingsEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("19. Engine Quality Settings", "Enabled", true, "Applies low-level Unity QualitySettings tweaks: disables soft particles and soft vegetation, reduces particle raycast budget. Minor visual change, significant GPU gain."); ParticleRaycastBudget = ((BaseUnityPlugin)this).Config.Bind<int>("19. Engine Quality Settings", "Particle Raycast Budget", 1024, new ConfigDescription("Max number of particle ray-casts per frame. Vanilla default is 4096. Lower = less CPU.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(64, 4096), Array.Empty<object>())); SkipIntroEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("20. Skip Intro", "Enabled", true, "Skips Iron Gate and Coffee Stain logo screens on game startup. Saves 5–10 seconds per launch."); FrameBudgetGuardEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("21. Frame Budget Guard", "Enabled", true, "Dynamically limits Time.maximumDeltaTime when heavy frame spikes are detected, preventing the Unity physics 'Death Spiral'. Converts hard freezes into brief slow-motion."); FrameBudgetThresholdMs = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Freeze Threshold (ms)", 28f, new ConfigDescription("1% Low frametime threshold in milliseconds. If worst frames exceed this, throttling activates. 28ms ≈ below 36 FPS on worst frames.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(16f, 100f), Array.Empty<object>())); FrameBudgetThrottledDelta = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Throttled MaxDeltaTime", 0.045f, new ConfigDescription("Time.maximumDeltaTime when throttling is active (freeze detected). Lower = physics accuracy is sacrificed more to preserve smoothness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.02f, 0.1f), Array.Empty<object>())); FrameBudgetNormalDelta = ((BaseUnityPlugin)this).Config.Bind<float>("21. Frame Budget Guard", "Normal MaxDeltaTime", 0.07f, new ConfigDescription("Time.maximumDeltaTime during normal gameplay. Unity default is 0.3333.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.03f, 0.2f), Array.Empty<object>())); } private void ApplyImmediateGraphicsSettings() { try { QualitySettings.shadowDistance = ConfigShadowDistance.Value; switch (ConfigShadowResolution.Value) { case 512: QualitySettings.shadowResolution = (ShadowResolution)0; break; case 1024: QualitySettings.shadowResolution = (ShadowResolution)1; break; case 2048: QualitySettings.shadowResolution = (ShadowResolution)2; break; case 4096: QualitySettings.shadowResolution = (ShadowResolution)3; break; default: QualitySettings.shadowResolution = (ShadowResolution)0; break; } QualitySettings.shadowCascades = ConfigShadowCascades.Value; Log.LogInfo((object)"[Graphics] Applied immediate graphics settings."); } catch (Exception ex) { Log.LogError((object)("[Graphics] Failed to apply settings: " + ex.Message)); } } private void OnDestroy() { Log.LogInfo((object)"Unpatching all Valheim Performance Overhaul methods."); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public class MemoryManager : MonoBehaviour { private float _cleanupTimer; private const float CLEANUP_INTERVAL = 60f; private float _forceGCTimer; private const float FORCE_GC_INTERVAL = 300f; public static MemoryManager Instance { get; private set; } private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log.LogInfo((object)"[MemoryManager] Initialized"); } private void Update() { _cleanupTimer += Time.deltaTime; _forceGCTimer += Time.deltaTime; if (_cleanupTimer >= 60f) { _cleanupTimer = 0f; PerformLightCleanup(); } if (_forceGCTimer >= 300f) { _forceGCTimer = 0f; if (ShouldPerformHeavyCleanup()) { PerformHeavyCleanup(); } } } private bool ShouldPerformHeavyCleanup() { if ((Object)(object)Player.m_localPlayer == (Object)null) { return true; } Player localPlayer = Player.m_localPlayer; if (!((Character)localPlayer).IsAttached() && !localPlayer.IsSleeping()) { return ((Character)localPlayer).InPlaceMode(); } return true; } private void PerformLightCleanup() { int num = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576); Resources.UnloadUnusedAssets(); int num2 = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576); if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[MemoryManager] Light cleanup: {num}MB -> {num2}MB"); } } private void PerformHeavyCleanup() { int num = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576); Resources.UnloadUnusedAssets(); GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); GC.Collect(); int num2 = (int)(GC.GetTotalMemory(forceFullCollection: false) / 1048576); Plugin.Log.LogInfo((object)$"[MemoryManager] Heavy cleanup: {num}MB -> {num2}MB"); } private void OnDestroy() { Instance = null; } } public class DistanceCuller : MonoBehaviour { private readonly List<MonoBehaviour> _culledComponents = new List<MonoBehaviour>(); private readonly List<Rigidbody> _trackedRigidbodies = new List<Rigidbody>(); private ZNetView _zNetView; private bool _isCulled; private bool _isCharacter; private Transform _transform; public float CullDistance = 80f; private float _cullDistanceSqr; private float _wakeUpDistanceSqr; private const float HYSTERESIS = 15f; private void Awake() { _transform = ((Component)this).transform; _zNetView = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)_zNetView == (Object)null || !_zNetView.IsValid()) { Object.Destroy((Object)(object)this); return; } _isCharacter = (Object)(object)((Component)this).GetComponent<Character>() != (Object)null; try { _cullDistanceSqr = (CullDistance + 15f) * (CullDistance + 15f); _wakeUpDistanceSqr = (CullDistance - 15f) * (CullDistance - 15f); CollectComponents(); if (Plugin.CullPhysicsEnabled.Value && !_isCharacter) { CollectRigidbodies(); } DistanceCullerManager.Instance?.RegisterCuller(this); } catch (Exception ex) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogError((object)("[DistanceCuller] Error in Awake: " + ex.Message)); } Object.Destroy((Object)(object)this); } } private void CollectComponents() { MonoBehaviour[] componentsInChildren = ((Component)this).GetComponentsInChildren<MonoBehaviour>(true); if (componentsInChildren == null) { return; } MonoBehaviour[] array = componentsInChildren; foreach (MonoBehaviour val in array) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)this) && !(val is ZNetView) && !(val is ZSyncTransform) && !(val is DistanceCuller) && !(val is Character) && !(val is Humanoid) && (!Plugin.AiThrottlingEnabled.Value || !(val is BaseAI))) { _culledComponents.Add(val); } } } private void CollectRigidbodies() { Rigidbody[] componentsInChildren = ((Component)this).GetComponentsInChildren<Rigidbody>(true); if (componentsInChildren == null) { return; } Rigidbody[] array = componentsInChildren; foreach (Rigidbody val in array) { if ((Object)(object)val != (Object)null && !_trackedRigidbodies.Contains(val)) { _trackedRigidbodies.Add(val); } } if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[DistanceCuller] Collected {_trackedRigidbodies.Count} Rigidbodies on {((Object)((Component)this).gameObject).name}"); } } public void ManagerUpdate(IReadOnlyList<Player> players) { if (players == null || players.Count == 0) { if (_isCulled) { SetComponentsEnabled(enabled: true); } } else { float minPlayerDistanceSqr = GetMinPlayerDistanceSqr(players); bool shouldCull = DetermineCullingState(minPlayerDistanceSqr); ApplyOwnershipLogic(shouldCull); } } private float GetMinPlayerDistanceSqr(IReadOnlyList<Player> players) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) float num = float.MaxValue; Vector3 position = _transform.position; for (int i = 0; i < players.Count; i++) { Player val = players[i]; if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).transform == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude < num) { num = sqrMagnitude; } } } return num; } private bool DetermineCullingState(float distSqr) { if (!_isCulled) { return distSqr > _cullDistanceSqr; } return distSqr > _wakeUpDistanceSqr; } private void ApplyOwnershipLogic(bool shouldCull) { if ((Object)(object)_zNetView == (Object)null) { return; } if (_zNetView.IsOwner()) { if (_isCulled != shouldCull) { SetComponentsEnabled(!shouldCull); } } else if (_isCulled) { SetComponentsEnabled(enabled: true); } } private void SetComponentsEnabled(bool enabled) { if (_isCulled == !enabled) { return; } _isCulled = !enabled; for (int num = _culledComponents.Count - 1; num >= 0; num--) { MonoBehaviour val = _culledComponents[num]; if ((Object)(object)val == (Object)null) { _culledComponents.RemoveAt(num); } else if (((Behaviour)val).enabled != enabled) { try { ((Behaviour)val).enabled = enabled; } catch (Exception ex) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogWarning((object)("[DistanceCuller] Failed to set " + ((object)val).GetType().Name + ": " + ex.Message)); } } } } if (!Plugin.CullPhysicsEnabled.Value || _isCharacter) { return; } for (int num2 = _trackedRigidbodies.Count - 1; num2 >= 0; num2--) { Rigidbody val2 = _trackedRigidbodies[num2]; if ((Object)(object)val2 == (Object)null) { _trackedRigidbodies.RemoveAt(num2); } else { try { if (enabled) { val2.WakeUp(); } else if (!val2.isKinematic && !val2.IsSleeping()) { val2.Sleep(); } } catch (Exception ex2) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogWarning((object)("[DistanceCuller] Rigidbody error: " + ex2.Message)); } } } } } private void OnDestroy() { try { DistanceCullerManager.Instance?.UnregisterCuller(this); if (_isCulled) { SetComponentsEnabled(enabled: true); } _culledComponents.Clear(); _trackedRigidbodies.Clear(); } catch (Exception ex) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogError((object)("[DistanceCuller] OnDestroy error: " + ex.Message)); } } } } public class DistanceCullerManager : MonoBehaviour { private static readonly List<Player> _globalPlayerCache = new List<Player>(); private readonly List<DistanceCuller> _cullers = new List<DistanceCuller>(512); private float _playerUpdateTimer; private float _cullingUpdateTimer; private const float PLAYER_UPDATE_INTERVAL = 1f; private const float CULLING_UPDATE_INTERVAL = 2f; public static DistanceCullerManager Instance { get; private set; } public static IReadOnlyList<Player> Players => _globalPlayerCache; private void Awake() { if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this) { Object.Destroy((Object)(object)this); return; } Instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); Plugin.Log.LogInfo((object)"[DistanceCullerManager] Initialized."); } public void RegisterCuller(DistanceCuller culler) { if ((Object)(object)culler != (Object)null && !_cullers.Contains(culler)) { _cullers.Add(culler); } } public void UnregisterCuller(DistanceCuller culler) { int num = _cullers.IndexOf(culler); if (num >= 0) { int index = _cullers.Count - 1; _cullers[num] = _cullers[index]; _cullers.RemoveAt(index); } } private void Update() { _playerUpdateTimer += Time.deltaTime; _cullingUpdateTimer += Time.deltaTime; if (_playerUpdateTimer >= 1f) { _playerUpdateTimer = 0f; _globalPlayerCache.Clear(); List<Player> allPlayers = Player.GetAllPlayers(); if (allPlayers != null) { _globalPlayerCache.AddRange(allPlayers); } } if (!(_cullingUpdateTimer >= 2f)) { return; } _cullingUpdateTimer = 0f; if (!Plugin.DistanceCullerEnabled.Value || _globalPlayerCache.Count == 0) { return; } for (int num = _cullers.Count - 1; num >= 0; num--) { DistanceCuller distanceCuller = _cullers[num]; if ((Object)(object)distanceCuller == (Object)null) { int index = _cullers.Count - 1; _cullers[num] = _cullers[index]; _cullers.RemoveAt(index); } else { distanceCuller.ManagerUpdate(_globalPlayerCache); } } } private void OnDestroy() { _cullers.Clear(); Instance = null; } } [HarmonyPatch] public static class GraphicsPatches { private static readonly FieldInfo _heightmapsListField = AccessTools.Field(typeof(Heightmap), "s_heightmaps"); [HarmonyPatch(typeof(GameCamera), "Awake")] [HarmonyPostfix] private static void ApplyPostProcessingSettings(GameCamera __instance) { if (!Plugin.GraphicsSettingsEnabled.Value) { return; } PostProcessingBehaviour component = ((Component)__instance).GetComponent<PostProcessingBehaviour>(); if (!((Object)(object)component == (Object)null) && !((Object)(object)component.profile == (Object)null)) { PostProcessingProfile profile = component.profile; ((PostProcessingModel)profile.bloom).enabled = Plugin.ConfigBloom.Value; ((PostProcessingModel)profile.screenSpaceReflection).enabled = Plugin.ConfigReflections.Value; if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)"[Graphics] Applied post-processing settings."); } } } [HarmonyPatch(typeof(Player), "OnSpawned")] [HarmonyPostfix] private static void ApplyInitialTerrainSettings() { if (!Plugin.GraphicsSettingsEnabled.Value || _heightmapsListField == null || !(_heightmapsListField.GetValue(null) is List<Heightmap> list)) { return; } if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[Graphics] Applying terrain quality to {list.Count} existing heightmaps..."); } float num = Mathf.Clamp(Plugin.ConfigTerrainQuality.Value, 0.1f, 2f); float heightmapPixelError = 5f / num; foreach (Heightmap item in list) { if ((Object)(object)item != (Object)null) { Terrain component = ((Component)item).GetComponent<Terrain>(); if ((Object)(object)component != (Object)null) { component.heightmapPixelError = heightmapPixelError; } } } } } public static class AnimatorOptimizer { [HarmonyPatch(typeof(Character), "Awake")] public static class Character_Awake_Patch { [HarmonyPostfix] private static void Postfix(Character __instance) { if (!((Object)(object)__instance == (Object)null) && Plugin.AnimatorOptimizationEnabled.Value && (Object)(object)((Component)__instance).GetComponent<CharacterAnimatorOptimizer>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<CharacterAnimatorOptimizer>(); } } } } public class CharacterAnimatorOptimizer : MonoBehaviour { private Character _character; private Animator _animator; private ZNetView _nview; private float _checkTimer; private const float CHECK_INTERVAL = 1f; private const float CULL_DIST_SQR = 3600f; private const float FAR_CULL_DIST_SQR = 10000f; private bool _isFullyCulled; private bool _isPartiallyCulled; private void Awake() { _character = ((Component)this).GetComponent<Character>(); _animator = ((Component)this).GetComponent<Animator>(); _nview = ((Component)this).GetComponent<ZNetView>(); _checkTimer = Random.Range(0f, 1f); } private void FixedUpdate() { _checkTimer += Time.fixedDeltaTime; if (!(_checkTimer < 1f)) { _checkTimer = 0f; Optimize(); } } private void Optimize() { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_character == (Object)null || (Object)(object)_animator == (Object)null) { Object.Destroy((Object)(object)this); } else { if (_character.IsPlayer() || ((Object)(object)_nview != (Object)null && !_nview.IsValid()) || (Object)(object)Player.m_localPlayer == (Object)null) { return; } Vector3 val = ((Component)_character).transform.position - ((Component)Player.m_localPlayer).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude > 10000f) { if (!_isFullyCulled) { ((Behaviour)_animator).enabled = false; _isFullyCulled = true; _isPartiallyCulled = false; if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[Animator] Fully disabled for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m"); } } } else if (sqrMagnitude > 3600f) { if (!_isPartiallyCulled || _isFullyCulled) { ((Behaviour)_animator).enabled = true; _animator.cullingMode = (AnimatorCullingMode)2; _isPartiallyCulled = true; _isFullyCulled = false; if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[Animator] CullCompletely for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m"); } } } else if (_isPartiallyCulled || _isFullyCulled) { ((Behaviour)_animator).enabled = true; _animator.cullingMode = (AnimatorCullingMode)0; _isPartiallyCulled = false; _isFullyCulled = false; if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)$"[Animator] Full animation for {((Object)_character).name} at {Mathf.Sqrt(sqrMagnitude):F1}m"); } } } } private void OnDestroy() { if ((Object)(object)_animator != (Object)null) { ((Behaviour)_animator).enabled = true; _animator.cullingMode = (AnimatorCullingMode)0; } } } [HarmonyPatch] public static class MinimapOptimizer { private static FieldInfo _smallRootField; private static FieldInfo _largeRootField; private static bool _fieldsFound; static MinimapOptimizer() { _smallRootField = AccessTools.Field(typeof(Minimap), "m_smallRoot"); _largeRootField = AccessTools.Field(typeof(Minimap), "m_largeRoot"); _fieldsFound = false; if (_smallRootField != null && _largeRootField != null) { _fieldsFound = true; return; } if (_smallRootField == null) { _smallRootField = AccessTools.Field(typeof(Minimap), "m_smallMapPanel"); } if (_largeRootField == null) { _largeRootField = AccessTools.Field(typeof(Minimap), "m_largeMapPanel"); } _fieldsFound = _smallRootField != null && _largeRootField != null; if (!_fieldsFound) { Plugin.Log.LogWarning((object)"[MinimapOptimizer] Could not find Minimap root fields. Optimization disabled."); } } [HarmonyPatch(typeof(Minimap), "Update")] [HarmonyPrefix] private static bool Minimap_Update_Prefix(Minimap __instance) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown if (!Plugin.MinimapOptimizationEnabled.Value) { return true; } if (!_fieldsFound) { return true; } if ((Object)(object)Player.m_localPlayer == (Object)null) { return true; } if ((int)__instance.m_mode == 2) { return true; } if ((int)__instance.m_mode == 1) { GameObject val = (GameObject)_smallRootField.GetValue(__instance); if ((Object)(object)val != (Object)null && !val.activeInHierarchy) { return false; } } return true; } [HarmonyPatch(typeof(Minimap), "UpdateDynamicPins")] [HarmonyPrefix] private static bool UpdateDynamicPins_Prefix(Minimap __instance) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.MinimapOptimizationEnabled.Value) { return true; } if ((int)__instance.m_mode == 0) { return false; } return true; } } } namespace ValheimPerformanceOverhaul.UI { public static class WelcomeMessage { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__2_0; internal void <ShowWelcomePanel>b__2_0() { Application.OpenURL("https://discord.com/users/1084209564653727804"); } } private static bool _shown = false; private static readonly string PREFS_KEY = "VPO_HideWelcome_v2.7.0"; [HarmonyPatch(typeof(FejdStartup), "Start")] [HarmonyPostfix] public static void ShowWelcomePanel() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b2: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_0420: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_049d: Unknown result type (might be due to invalid IL or missing references) //IL_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_04dd: Unknown result type (might be due to invalid IL or missing references) //IL_04f6: Unknown result type (might be due to invalid IL or missing references) //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Expected O, but got Unknown //IL_052b: Unknown result type (might be due to invalid IL or missing references) //IL_0543: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) //IL_0561: Unknown result type (might be due to invalid IL or missing references) //IL_057e: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05c0: Unknown result type (might be due to invalid IL or missing references) //IL_05cc: Unknown result type (might be due to invalid IL or missing references) //IL_05d6: Expected O, but got Unknown //IL_0327: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Expected O, but got Unknown if (_shown || PlayerPrefs.GetInt(PREFS_KEY, 0) == 1) { return; } _shown = true; GameObject canvasGO = new GameObject("WelcomeCanvas_VPO"); canvasGO.layer = 5; Canvas obj = canvasGO.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 9999; canvasGO.AddComponent<CanvasScaler>(); canvasGO.AddComponent<GraphicRaycaster>(); Object.DontDestroyOnLoad((Object)(object)canvasGO); GameObject val = MakeRect("Background", canvasGO.transform, new Color(0.05f, 0.05f, 0.05f, 0.96f)); RectTransform component = val.GetComponent<RectTransform>(); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); component.anchorMax = val2; component.anchorMin = val2; component.sizeDelta = new Vector2(550f, 480f); component.anchoredPosition = Vector2.zero; GameObject val3 = MakeRect("Header", val.transform, new Color(0.1f, 0.1f, 0.1f, 1f)); Stretch(val3, new Vector2(0f, 0.86f), Vector2.one); MakeText("Title", val3.transform, "ValheimPerformanceOverhaul", 22, new Color(1f, 0.85f, 0.4f), (FontStyle)1); Text obj2 = MakeText("Body", val.transform, "Hi! Thank you for using my mod.\nThe mod is in beta, so I'd love to hear any feedback:\nbugs, suggestions, logs - anything.\n\nПривет! Спасибо за использование моего мода.\nМод находится в бете, поэтому я буду рад любым отзывам:\nбагам, предложениям, логам - чему угодно.\n\nMy Discord account / Мой аккаунт Discord:", 15, Color.white, (FontStyle)0); obj2.lineSpacing = 1.2f; obj2.alignment = (TextAnchor)1; SetAnchors(((Component)obj2).gameObject, new Vector2(0f, 0.35f), new Vector2(1f, 0.86f), new Vector2(20f, 0f), new Vector2(-20f, -15f)); GameObject val4 = MakeRect("Link", val.transform, Color.clear); SetAnchors(val4, new Vector2(0f, 0.25f), new Vector2(1f, 0.35f), new Vector2(20f, 0f), new Vector2(-20f, 0f)); MakeText("LinkText", val4.transform, "https://discord.com/users/1084209564653727804 (skarif_q)", 15, new Color(0.35f, 0.7f, 1f), (FontStyle)0); RectTransform component2 = MakeRect("Underline", val4.transform, new Color(0.35f, 0.7f, 1f)).GetComponent<RectTransform>(); component2.anchorMin = new Vector2(0.05f, 0f); component2.anchorMax = new Vector2(0.95f, 0f); component2.sizeDelta = new Vector2(0f, 1.5f); component2.anchoredPosition = new Vector2(0f, 3f); Image component3 = val4.GetComponent<Image>(); Color highlight = new Color(0.35f, 0.7f, 1f, 0.15f); Color pressed = new Color(0.35f, 0.7f, 1f, 0.3f); object obj3 = <>c.<>9__2_0; if (obj3 == null) { UnityAction val5 = delegate { Application.OpenURL("https://discord.com/users/1084209564653727804"); }; <>c.<>9__2_0 = val5; obj3 = (object)val5; } MakeButton(val4, component3, highlight, pressed, (UnityAction)obj3); MakeSeparator("Sep1", val.transform, 0.22f); MakeSeparator("Sep2", val.transform, 0.14f); bool dontShow = false; SetPivotAnchor(((Component)MakeText("Label", val.transform, "Don't show anymore / Больше не показывать", 13, new Color(0.75f, 0.75f, 0.75f), (FontStyle)0)).gameObject, new Vector2(0.5f, 0f), new Vector2(280f, 30f), new Vector2(20f, 85f)); GameObject val6 = MakeRect("Box", val.transform, new Color(0.25f, 0.25f, 0.25f, 1f)); SetPivotAnchor(val6, new Vector2(0.5f, 0f), new Vector2(18f, 18f), new Vector2(-135f, 85f)); GameObject tickGO = ((Component)MakeText("Tick", val6.transform, "v", 13, new Color(0.35f, 0.7f, 1f), (FontStyle)1)).gameObject; tickGO.SetActive(false); GameObject obj4 = MakeRect("CheckHit", val.transform, Color.clear); SetPivotAnchor(obj4, new Vector2(0.5f, 0f), new Vector2(320f, 30f), new Vector2(0f, 85f)); Image boxImg = val6.GetComponent<Image>(); MakeButton(obj4, obj4.GetComponent<Image>(), new Color(1f, 1f, 1f, 0.05f), new Color(1f, 1f, 1f, 0.1f), (UnityAction)delegate { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) dontShow = !dontShow; tickGO.SetActive(dontShow); ((Graphic)boxImg).color = (dontShow ? new Color(0.1f, 0.25f, 0.45f, 1f) : new Color(0.25f, 0.25f, 0.25f, 1f)); }); GameObject val7 = MakeRect("Close", val.transform, new Color(0.65f, 0.15f, 0.15f, 1f)); SetPivotAnchor(val7, new Vector2(0.5f, 0f), new Vector2(140f, 34f), new Vector2(0f, 20f)); MakeText("CloseText", val7.transform, "Close / Закрыть", 15, Color.white, (FontStyle)0); MakeButton(val7, val7.GetComponent<Image>(), new Color(0.85f, 0.25f, 0.25f, 1f), new Color(0.45f, 0.08f, 0.08f, 1f), (UnityAction)delegate { if (dontShow) { PlayerPrefs.SetInt(PREFS_KEY, 1); } Object.Destroy((Object)(object)canvasGO); }); } private static GameObject MakeRect(string name, Transform parent, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown GameObject val = new GameObject(name) { layer = 5 }; val.transform.SetParent(parent, false); val.AddComponent<RectTransform>(); ((Graphic)val.AddComponent<Image>()).color = color; return val; } private static Text MakeText(string name, Transform parent, string text, int size, Color color, FontStyle style = 0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); val.layer = 5; val.transform.SetParent(parent, false); val.AddComponent<RectTransform>(); Text obj = val.AddComponent<Text>(); obj.text = text; obj.font = GetFont(); obj.fontSize = size; ((Graphic)obj).color = color; obj.fontStyle = style; obj.alignment = (TextAnchor)4; Stretch(val, Vector2.zero, Vector2.one); return obj; } private static void MakeButton(GameObject go, Image target, Color highlight, Color pressed, UnityAction action) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) Button obj = go.AddComponent<Button>(); ((Selectable)obj).targetGraphic = (Graphic)(object)target; ColorBlock colors = ((Selectable)obj).colors; ((ColorBlock)(ref colors)).normalColor = ((Graphic)target).color; ((ColorBlock)(ref colors)).highlightedColor = highlight; ((ColorBlock)(ref colors)).pressedColor = pressed; ((Selectable)obj).colors = colors; ((UnityEvent)obj.onClick).AddListener(action); } private static void MakeSeparator(string name, Transform parent, float anchorY) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) RectTransform component = MakeRect(name, parent, new Color(1f, 1f, 1f, 0.12f)).GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, anchorY); component.anchorMax = new Vector2(1f, anchorY); component.sizeDelta = new Vector2(0f, 1f); component.anchoredPosition = Vector2.zero; } private static void Stretch(GameObject go, Vector2 min, Vector2 max) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; Vector2 offsetMin = (component.offsetMax = Vector2.zero); component.offsetMin = offsetMin; } private static void SetAnchors(GameObject go, Vector2 min, Vector2 max, Vector2 offsetMin, Vector2 offsetMax) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent<RectTransform>(); component.anchorMin = min; component.anchorMax = max; component.offsetMin = offsetMin; component.offsetMax = offsetMax; } private static void SetPivotAnchor(GameObject go, Vector2 anchor, Vector2 size, Vector2 pos) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) RectTransform component = go.GetComponent<RectTransform>(); Vector2 anchorMin = (component.anchorMax = anchor); component.anchorMin = anchorMin; component.sizeDelta = size; component.anchoredPosition = pos; } private static Font GetFont() { Font[] array = Resources.FindObjectsOfTypeAll<Font>(); foreach (Font val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name.Contains("Averia")) { return val; } } return Resources.GetBuiltinResource<Font>("Arial.ttf"); } } } namespace ValheimPerformanceOverhaul.AI { public class AIOptimizer : MonoBehaviour { private BaseAI _ai; private ZNetView _nview; private Character _character; private float _distCheckTimer; private const float DIST_CHECK_INTERVAL = 1f; private float _closestPlayerDistSqr = 1000000f; private Character _cachedEnemy; private float _lastEnemyCheck; private float _lastAttackCheck; private float _lastPathUpdate; private Vector3 _lastPathTargetPos; private readonly Dictionary<int, (bool visible, float time)> _losCache = new Dictionary<int, (bool, float)>(); private const int MAX_LOS_CACHE_SIZE = 100; private const float LOS_CACHE_TIMEOUT = 5f; private float _losCleanupTimer; private Rigidbody _rigidbody; private Collider[] _colliders; private bool _physicsActive = true; private Animator _animator; private bool _animatorActive = true; private float _originalAnimatorSpeed = 1f; private void Awake() { _ai = ((Component)this).GetComponent<BaseAI>(); _nview = ((Component)this).GetComponent<ZNetView>(); _character = ((Component)this).GetComponent<Character>(); _rigidbody = ((Component)this).GetComponent<Rigidbody>(); _colliders = ((Component)this).GetComponentsInChildren<Collider>(); _animator = ((Component)this).GetComponentInChildren<Animator>(); if ((Object)(object)_animator != (Object)null) { _originalAnimatorSpeed = _animator.speed; } } private void OnEnable() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) _closestPlayerDistSqr = 0f; _distCheckTimer = 1f; _cachedEnemy = null; _lastEnemyCheck = -100f; _lastAttackCheck = -100f; _lastPathUpdate = -100f; _lastPathTargetPos = Vector3.zero; _losCache.Clear(); _losCleanupTimer = 0f; _physicsActive = true; _animatorActive = true; if ((Object)(object)_animator != (Object)null) { ((Behaviour)_animator).enabled = true; _animator.speed = _originalAnimatorSpeed; } if ((Object)(object)_rigidbody != (Object)null && !_rigidbody.isKinematic) { _rigidbody.WakeUp(); } } public float GetDistanceToPlayer() { return Mathf.Sqrt(_closestPlayerDistSqr); } private void FixedUpdate() { if ((Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner()) { return; } _distCheckTimer += Time.fixedDeltaTime; if (!(_distCheckTimer < 1f)) { _distCheckTimer = 0f; UpdateClosestPlayerDistance(); UpdatePhysicsAndAnimator(); _losCleanupTimer += 1f; if (_losCleanupTimer >= 10f) { _losCleanupTimer = 0f; CleanupLOSCache(); } } } private void UpdateClosestPlayerDistance() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) _closestPlayerDistSqr = 1000000f; Vector3 val; if ((Object)(object)Player.m_localPlayer != (Object)null) { val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position; _closestPlayerDistSqr = ((Vector3)(ref val)).sqrMagnitude; } foreach (Player allPlayer in Player.GetAllPlayers()) { if (!((Object)(object)allPlayer == (Object)null)) { val = ((Component)allPlayer).transform.position - ((Component)this).transform.position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude < _closestPlayerDistSqr) { _closestPlayerDistSqr = sqrMagnitude; } } } } private void UpdatePhysicsAndAnimator() { if (!Plugin.AiThrottlingEnabled.Value) { return; } float distanceToPlayer = GetDistanceToPlayer(); if (Plugin.CullPhysicsEnabled.Value && (Object)(object)_rigidbody != (Object)null) { bool flag = distanceToPlayer < 120f; if (flag != _physicsActive) { SetPhysicsActive(flag); } } if (!Plugin.AnimatorOptimizationEnabled.Value || !((Object)(object)_animator != (Object)null)) { return; } if (distanceToPlayer > 120f) { if (_animatorActive) { ((Behaviour)_animator).enabled = false; _animatorActive = false; } } else if (distanceToPlayer > 60f) { if (!_animatorActive) { ((Behaviour)_animator).enabled = true; _animatorActive = true; } _animator.speed = _originalAnimatorSpeed * 0.5f; } else { if (!_animatorActive) { ((Behaviour)_animator).enabled = true; _animatorActive = true; } _animator.speed = _originalAnimatorSpeed; } } private void SetPhysicsActive(bool active) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_character != (Object)null) { return; } if ((Object)(object)_rigidbody != (Object)null) { if (!active && !_rigidbody.isKinematic) { _rigidbody.velocity = Vector3.zero; _rigidbody.angularVelocity = Vector3.zero; } _rigidbody.isKinematic = !active; } if (_colliders != null) { Collider[] colliders = _colliders; foreach (Collider val in colliders) { if ((Object)(object)val != (Object)null) { val.enabled = active; } } } _physicsActive = active; } public bool ShouldCheckEnemy(out Character currentEnemy) { currentEnemy = _cachedEnemy; float time = Time.time; float interval = GetInterval(0.5f, 2f); if (time - _lastEnemyCheck >= interval) { _lastEnemyCheck = time; return true; } return false; } public void UpdateCachedEnemy(Character enemy) { _cachedEnemy = enemy; } public bool ShouldCheckAttack() { float time = Time.time; float interval = GetInterval(0.3f, 1f); if (time - _lastAttackCheck >= interval) { _lastAttackCheck = time; return true; } return false; } public bool ShouldUpdatePath(Vector3 targetPos) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; float interval = GetInterval(0.5f, 2f); bool num = time - _lastPathUpdate >= interval; Vector3 val = targetPos - _lastPathTargetPos; bool flag = ((Vector3)(ref val)).sqrMagnitude >= 4f; if (num || flag) { _lastPathUpdate = time; _lastPathTargetPos = targetPos; return true; } return false; } public bool GetCachedLOS(Character target, out bool visible) { visible = false; if ((Object)(object)target == (Object)null) { return false; } int hashCode = ((object)target).GetHashCode(); float time = Time.time; if (_losCache.TryGetValue(hashCode, out (bool, float) value) && time - value.Item2 < 0.5f) { (visible, _) = value; return true; } return false; } public void SetCachedLOS(Character target, bool visible) { if (!((Object)(object)target == (Object)null)) { _losCache[((object)target).GetHashCode()] = (visible, Time.time); } } private float GetInterval(float min, float max) { if (_closestPlayerDistSqr < 1600f) { return min; } if (_closestPlayerDistSqr > 14400f) { return max; } float num = (_closestPlayerDistSqr - 1600f) / 12800f; return Mathf.Lerp(min, max, num); } private void CleanupLOSCache() { if (Time.frameCount % 600 != 0) { return; } float time = Time.time; List<int> list = new List<int>(); foreach (KeyValuePair<int, (bool, float)> item in _losCache) { if (time - item.Value.Item2 > 10f) { list.Add(item.Key); } } foreach (int item2 in list) { _losCache.Remove(item2); } } private void OnDestroy() { _losCache.Clear(); } } [HarmonyPatch] public static class AIPatches { private const float AI_ACTIVATION_RADIUS = 60f; private const float AI_DEACTIVATION_RADIUS = 80f; private const float AI_SLOW_UPDATE_INTERVAL = 5f; private static readonly Dictionary<BaseAI, float> _lastSlowUpdate = new Dictionary<BaseAI, float>(); [HarmonyPatch(typeof(BaseAI), "Awake")] [HarmonyPostfix] private static void BaseAI_Awake_Postfix(BaseAI __instance) { if ((Object)(object)((Component)__instance).GetComponent<AIOptimizer>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<AIOptimizer>(); } Character component = ((Component)__instance).GetComponent<Character>(); if ((Object)(object)component != (Object)null && component.IsTamed() && (Object)(object)((Component)__instance).GetComponent<TamedIdleOptimizer>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<TamedIdleOptimizer>(); } _lastSlowUpdate[__instance] = Time.time; } [HarmonyPatch(typeof(MonsterAI), "UpdateAI")] [HarmonyPrefix] private static bool MonsterAI_UpdateAI_Prefix(MonsterAI __instance, float dt) { if (!Plugin.AiThrottlingEnabled.Value) { return true; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { return true; } AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component == (Object)null) { return true; } float distanceToPlayer = component.GetDistanceToPlayer(); if (distanceToPlayer <= 60f) { return true; } if (distanceToPlayer > 60f) { if (!_lastSlowUpdate.TryGetValue((BaseAI)(object)__instance, out var value)) { _lastSlowUpdate[(BaseAI)(object)__instance] = Time.time; return false; } if (Time.time - value < 5f) { return false; } _lastSlowUpdate[(BaseAI)(object)__instance] = Time.time; if (Plugin.DebugLoggingEnabled.Value && Time.frameCount % 300 == 0) { Plugin.Log.LogInfo((object)$"[AI] Slow update for {((Object)__instance).name} at {distanceToPlayer:F1}m"); } return true; } return true; } [HarmonyPatch(typeof(BaseAI), "OnDestroy")] [HarmonyPostfix] private static void BaseAI_OnDestroy_Postfix(BaseAI __instance) { _lastSlowUpdate.Remove(__instance); } [HarmonyPatch(typeof(BaseAI), "FindEnemy")] [HarmonyPrefix] private static bool FindEnemy_Prefix(BaseAI __instance, ref Character __result) { if (!Plugin.AiThrottlingEnabled.Value) { return true; } AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component != (Object)null) { if (component.GetDistanceToPlayer() < 60f) { return true; } if (!component.ShouldCheckEnemy(out __result)) { return false; } } return true; } [HarmonyPatch(typeof(BaseAI), "FindEnemy")] [HarmonyPostfix] private static void FindEnemy_Postfix(BaseAI __instance, Character __result) { if (Plugin.AiThrottlingEnabled.Value) { AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component != (Object)null) { component.UpdateCachedEnemy(__result); } } } [HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Character) })] [HarmonyPrefix] private static bool CanSeeTarget_Prefix(BaseAI __instance, Character target, ref bool __result) { if (!Plugin.AiThrottlingEnabled.Value) { return true; } AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component != (Object)null) { if (component.GetDistanceToPlayer() < 60f) { return true; } if (component.GetCachedLOS(target, out var visible)) { __result = visible; return false; } if (component.GetDistanceToPlayer() > 40f) { __result = false; component.SetCachedLOS(target, visible: false); return false; } } return true; } [HarmonyPatch(typeof(BaseAI), "CanSeeTarget", new Type[] { typeof(Character) })] [HarmonyPostfix] private static void CanSeeTarget_Postfix(BaseAI __instance, Character target, bool __result) { if (Plugin.AiThrottlingEnabled.Value) { AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component != (Object)null && (Object)(object)target != (Object)null) { component.SetCachedLOS(target, __result); } } } [HarmonyPatch(typeof(MonsterAI), "DoAttack")] [HarmonyPrefix] private static bool DoAttack_Prefix(MonsterAI __instance) { if (!Plugin.AiThrottlingEnabled.Value) { return true; } AIOptimizer component = ((Component)__instance).GetComponent<AIOptimizer>(); if ((Object)(object)component != (Object)null) { if (component.GetDistanceToPlayer() < 60f) { return true; } return component.ShouldCheckAttack(); } return true; } [HarmonyPatch(typeof(ZNet), "Update")] [HarmonyPostfix] private static void CleanupSlowUpdateCache() { if (!Plugin.AiThrottlingEnabled.Value || Time.frameCount % 3600 != 0) { return; } List<BaseAI> list = new List<BaseAI>(); float time = Time.time; foreach (KeyValuePair<BaseAI, float> item in _lastSlowUpdate) { if ((Object)(object)item.Key == (Object)null || time - item.Value > 60f) { list.Add(item.Key); } } foreach (BaseAI item2 in list) { _lastSlowUpdate.Remove(item2); } if (Plugin.DebugLoggingEnabled.Value && list.Count > 0) { Plugin.Log.LogInfo((object)$"[AI] Cleaned {list.Count} stale slow update entries"); } } } public class TamedIdleOptimizer : MonoBehaviour { private Character _character; private BaseAI _baseAI; private MonsterAI _monsterAI; private ZNetView _nview; private Animator _animator; private Rigidbody _rigidbody; private Collider[] _colliders; private bool _isInIdleMode; private float _lastStateChange; private float _lastCombatTime; private float _lastCheckTime; private bool _originalAIEnabled = true; private float _originalAnimatorSpeed = 1f; private bool _originalRigidbodyKinematic; private bool[] _originalColliderStates; private const float CHECK_INTERVAL = 1f; private const float MIN_TIME_IN_COMBAT = 5f; private const float STATE_CHANGE_COOLDOWN = 2f; private const float BASE_DETECTION_RADIUS = 30f; private void Awake() { _character = ((Component)this).GetComponent<Character>(); _baseAI = ((Component)this).GetComponent<BaseAI>(); _monsterAI = ((Component)this).GetComponent<MonsterAI>(); _nview = ((Component)this).GetComponent<ZNetView>(); _animator = ((Component)this).GetComponentInChildren<Animator>(); _rigidbody = ((Component)this).GetComponent<Rigidbody>(); _colliders = ((Component)this).GetComponentsInChildren<Collider>(); if ((Object)(object)_animator != (Object)null) { _originalAnimatorSpeed = _animator.speed; } if ((Object)(object)_rigidbody != (Object)null) { _originalRigidbodyKinematic = _rigidbody.isKinematic; } if (_colliders != null && _colliders.Length != 0) { _originalColliderStates = new bool[_colliders.Length]; for (int i = 0; i < _colliders.Length; i++) { _originalColliderStates[i] = (Object)(object)_colliders[i] != (Object)null && _colliders[i].enabled; } } _lastCheckTime = Time.time; } private void Update() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!Plugin.TamedIdleOptimizationEnabled.Value) { if (_isInIdleMode) { ExitIdleMode(); } return; } float time = Time.time; if (time - _lastCheckTime < 1f) { return; } _lastCheckTime = time; if ((Object)(object)_character != (Object)null && _character.InAttack()) { _lastCombatTime = time; } if (_isInIdleMode) { if (ShouldExitIdle()) { ExitIdleMode(); } } else if (ShouldEnterIdle()) { EnterIdleMode(); } } private bool ShouldEnterIdle() { if (Time.time - _lastStateChange < 2f) { return false; } if ((Object)(object)_baseAI != (Object)null && (Object)(object)_baseAI.GetTargetCreature() != (Object)null) { return false; } if (IsTamed() && !IsInCombat() && IsInsideBase() && !HasFollowTarget()) { return Time.time - _lastCombatTime > Plugin.TamedIdleDistanceFromCombat.Value; } return false; } private bool ShouldExitIdle() { if (!IsInCombat() && !HasFollowTarget() && !PlayerInteracted()) { return !IsInsideBase(); } return true; } private bool IsTamed() { if ((Object)(object)_character == (Object)null) { return false; } return _character.IsTamed(); } private bool IsInCombat() { if ((Object)(object)_character == (Object)null) { return false; } if (_character.InAttack()) { return true; } _character.GetHealth(); _character.GetMaxHealth(); if ((Object)(object)_baseAI != (Object)null) { if (_baseAI.IsAlerted()) { return true; } if ((Object)(object)_baseAI.GetTargetCreature() != (Object)null) { return true; } if (_baseAI.HaveTarget()) { return true; } } if ((Object)(object)_monsterAI != (Object)null) { Character targetCreature = ((BaseAI)_monsterAI).GetTargetCreature(); if ((Object)(object)targetCreature != (Object)null && !targetCreature.IsDead()) { return true; } } return false; } private bool IsInsideBase() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false)) { return true; } bool result = false; Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, Plugin.TamedIdleBaseDetectionRadius.Value); for (int i = 0; i < array.Length; i++) { if ((Object)(object)((Component)array[i]).GetComponentInParent<Piece>() != (Object)null) { result = true; break; } } return result; } private bool HasFollowTarget() { if ((Object)(object)_monsterAI == (Object)null) { return false; } return (Object)(object)_monsterAI.GetFollowTarget() != (Object)null; } private bool PlayerInteracted() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer != (Object)null) { Vector3 val = ((Component)Player.m_localPlayer).transform.position - ((Component)this).transform.position; if (((Vector3)(ref val)).sqrMagnitude < 25f) { return true; } } return false; } private void EnterIdleMode() { if (_isInIdleMode) { return; } if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[TamedIdleOptimizer] " + ((Object)((Component)this).gameObject).name + " entering IDLE MODE")); } if ((Object)(object)_baseAI != (Object)null) { _originalAIEnabled = ((Behaviour)_baseAI).enabled; ((Behaviour)_baseAI).enabled = false; _baseAI.StopMoving(); if ((Object)(object)_monsterAI != (Object)null) { _monsterAI.SetFollowTarget((GameObject)null); } } if ((Object)(object)_animator != (Object)null) { _originalAnimatorSpeed = _animator.speed; _animator.speed = 0f; ((Behaviour)_animator).enabled = false; } _isInIdleMode = true; _lastStateChange = Time.time; } private void ExitIdleMode() { if (_isInIdleMode) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[TamedIdleOptimizer] " + ((Object)((Component)this).gameObject).name + " exiting IDLE MODE")); } if ((Object)(object)_baseAI != (Object)null) { ((Behaviour)_baseAI).enabled = _originalAIEnabled; } if ((Object)(object)_animator != (Object)null) { ((Behaviour)_animator).enabled = true; _animator.speed = _originalAnimatorSpeed; } _isInIdleMode = false; _lastStateChange = Time.time; } } public bool IsInIdleMode() { return _isInIdleMode; } private void OnDestroy() { if (_isInIdleMode) { ExitIdleMode(); } } } } namespace ValheimPerformanceOverhaul.ObjectPooling { [HarmonyPatch] public static class ObjectPoolingPatches { private static readonly FieldInfo _zdoField = AccessTools.Field(typeof(ZNetView), "m_zdo"); private static readonly FieldInfo _instancesField = AccessTools.Field(typeof(ZNetScene), "m_instances"); private static readonly HashSet<string> _poolingExclusions = new HashSet<string> { "spear_bronze", "spear_chitin", "spear_flint", "spear_carapace", "arrow_wood", "arrow_fire", "arrow_flint", "arrow_bronze", "arrow_iron", "arrow_silver", "arrow_poison", "arrow_frost", "arrow_needle", "arrow_obsidian", "arrow_carapace", "bomb", "tankard", "tankard_odin", "drop_stone", "drop_wood" }; private static bool CanPoolObject(GameObject obj) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Invalid comparison between Unknown and I4 if ((Object)(object)obj == (Object)null) { return false; } string text = ((Object)obj).name.Replace("(Clone)", "").ToLower(); if (_poolingExclusions.Contains(text)) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[ObjectPooling] Excluded from pooling: " + text)); } return false; } ItemDrop component = obj.GetComponent<ItemDrop>(); if ((Object)(object)component != (Object)null && component.m_itemData != null) { ItemData itemData = component.m_itemData; if (itemData.m_shared != null && (int)itemData.m_shared.m_itemType == 3 && itemData.m_shared.m_attack != null && (Object)(object)itemData.m_shared.m_attack.m_attackProjectile != (Object)null) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[ObjectPooling] Excluded projectile weapon: " + text)); } return false; } } return true; } [HarmonyPatch(typeof(ZNetScene), "CreateObject")] [HarmonyPrefix] private static bool CreateObjectPrefix(ZNetScene __instance, ZDO zdo, ref GameObject __result) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.ObjectPoolingEnabled.Value) { return true; } GameObject prefab = __instance.GetPrefab(zdo.GetPrefab()); if ((Object)(object)prefab == (Object)null) { return true; } if ((Object)(object)prefab.GetComponent<ItemDrop>() == (Object)null) { return true; } if (!CanPoolObject(prefab)) { return true; } if (ObjectPoolManager.TryGetObject(((Object)prefab).name, out var instance)) { if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)("[ObjectPooling] Pool returned null for " + ((Object)prefab).name + ", using original spawn")); return true; } if (instance.activeSelf) { Plugin.Log.LogError((object)("[ObjectPooling] Pool returned active object for " + ((Object)prefab).name + "! Using original spawn")); return true; } ZNetView component = instance.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null) { ZDO zDO = component.GetZDO(); if (zDO != null && zDO.IsValid()) { Plugin.Log.LogError((object)("[ObjectPooling] Pool returned object with valid ZDO for " + ((Object)prefab).name + "! Using original spawn")); return true; } } try { instance.transform.position = zdo.GetPosition(); instance.transform.rotation = zdo.GetRotation(); } catch (Exception ex) { Plugin.Log.LogError((object)("[ObjectPooling] Error setting transform: " + ex.Message)); return true; } instance.SetActive(true); PooledObject pooledObject = instance.GetComponent<PooledObject>(); if ((Object)(object)pooledObject == (Object)null) { pooledObject = instance.AddComponent<PooledObject>(); } pooledObject.Initialize(((Object)prefab).name); if ((Object)(object)component != (Object)null) { try { ZDO zDO2 = component.GetZDO(); if (zDO2 != null && zDO2.IsValid()) { component.ResetZDO(); } if (_zdoField != null) { _zdoField.SetValue(component, zdo); } if (_instancesField != null) { Dictionary<ZDO, ZNetView> dictionary = (Dictionary<ZDO, ZNetView>)_instancesField.GetValue(__instance); if (dictionary != null) { if (dictionary.ContainsKey(zdo)) { Plugin.Log.LogError((object)("[ObjectPooling] ZDO already exists in instances! " + ((Object)prefab).name)); instance.SetActive(false); ObjectPoolManager.ReturnObject(instance); return true; } dictionary[zdo] = component; } } } catch (Exception ex2) { Plugin.Log.LogError((object)("[ObjectPooling] Error setting up ZNetView: " + ex2.Message)); instance.SetActive(false); ObjectPoolManager.ReturnObject(instance); return true; } } __result = instance; if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[ObjectPooling] Reused object: " + ((Object)prefab).name)); } return false; } return true; } [HarmonyPatch(typeof(ZNetScene), "Destroy")] [HarmonyPrefix] private static bool DestroyPrefix(ZNetScene __instance, GameObject go) { if (!Plugin.ObjectPoolingEnabled.Value || (Object)(object)go == (Object)null) { return true; } if ((Object)(object)go.GetComponent<ItemDrop>() == (Object)null) { return true; } if (!CanPoolObject(go)) { return true; } ZNetView component = go.GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null) { return true; } try { if (_instancesField != null) { Dictionary<ZDO, ZNetView> dictionary = (Dictionary<ZDO, ZNetView>)_instancesField.GetValue(__instance); ZDO zDO = component.GetZDO(); if (dictionary != null && zDO != null) { dictionary.Remove(zDO); if (zDO.IsOwner() && ZDOMan.instance != null) { ZDOMan.instance.DestroyZDO(zDO); } } if (component.GetZDO() != null) { component.ResetZDO(); } } ObjectPoolManager.ReturnObject(go); if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[ObjectPooling] Returned to pool: " + ((Object)go).name)); } return false; } catch (Exception ex) { Plugin.Log.LogError((object)("[ObjectPooling] Error returning to pool: " + ex.Message)); return true; } } [HarmonyPatch(typeof(ZNetScene), "Update")] [HarmonyPostfix] private static void PerformPeriodicMaintenance() { if (Plugin.ObjectPoolingEnabled.Value) { if (Time.frameCount % 3600 == 0) { ObjectPoolManager.PerformMaintenance(); } if (Plugin.DebugLoggingEnabled.Value && Time.frameCount % 18000 == 0) { ObjectPoolManager.LogPoolStats(); } } } [HarmonyPatch(typeof(ZNet), "Shutdown")] [HarmonyPostfix] private static void ClearPoolOnShutdown() { if (Plugin.ObjectPoolingEnabled.Value) { ObjectPoolManager.Clear(); } } } public static class ObjectPoolManager { private static readonly Dictionary<int, Queue<GameObject>> _pools = new Dictionary<int, Queue<GameObject>>(); private static readonly HashSet<GameObject> _objectsInUse = new HashSet<GameObject>(); private static Transform _poolParent; private const int MAX_POOL_SIZE = 100; private static readonly object _lockObject = new object(); private static readonly List<GameObject> _tempActiveCheck = new List<GameObject>(16); public static void Initialize() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if (!((Object)(object)_poolParent != (Object)null)) { GameObject val = new GameObject("_VPO_ObjectPool"); _poolParent = val.transform; Object.DontDestroyOnLoad((Object)val); lock (_lockObject) { _pools.Clear(); _objectsInUse.Clear(); } Plugin.Log.LogInfo((object)"[ObjectPooling] Manager initialized."); } } public static bool TryGetObject(string prefabName, out GameObject instance) { int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName); instance = null; lock (_lockObject) { if (_pools.TryGetValue(stableHashCode, out var value) && value.Count > 0) { int num = 0; while (value.Count > 0 && num < 5) { instance = value.Dequeue(); num++; if ((Object)(object)instance == (Object)null) { Plugin.Log.LogWarning((object)("[ObjectPooling] Null object in pool for " + prefabName)); continue; } if (instance.activeSelf) { Plugin.Log.LogError((object)("[ObjectPooling] Object already active: " + prefabName)); _objectsInUse.Remove(instance); continue; } ZNetView component = instance.GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null) { ZDO zDO = component.GetZDO(); if (zDO != null && zDO.IsValid()) { Plugin.Log.LogError((object)("[ObjectPooling] Object has valid ZDO: " + prefabName)); continue; } } if (_objectsInUse.Contains(instance)) { Plugin.Log.LogError((object)("[ObjectPooling] Object already in use: " + prefabName)); continue; } _objectsInUse.Add(instance); ResetObjectState(instance); return true; } instance = null; return false; } } return false; } public static void ReturnObject(GameObject instance) { if ((Object)(object)instance == (Object)null) { return; } lock (_lockObject) { if (!_objectsInUse.Contains(instance)) { if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogInfo((object)("[ObjectPooling] Object not from pool, destroying: " + ((Object)instance).name)); } Object.Destroy((Object)(object)instance); return; } _objectsInUse.Remove(instance); CleanupObjectState(instance); instance.transform.SetParent((Transform)null); instance.SetActive(false); instance.transform.SetParent(_poolParent); PooledObject component = instance.GetComponent<PooledObject>(); int key; if ((Object)(object)component != (Object)null) { key = component.PrefabHash; component.MarkAsAvailable(); } else { key = StringExtensionMethods.GetStableHashCode(((Object)instance).name.Replace("(Clone)", "")); } if (!_pools.TryGetValue(key, out var value)) { value = new Queue<GameObject>(); _pools.Add(key, value); } if (value.Count < 100) { value.Enqueue(instance); return; } Object.Destroy((Object)(object)instance); if (Plugin.DebugLoggingEnabled.Value) { Plugin.Log.LogWarning((object)$"[ObjectPooling] Pool full ({100}), destroying: {((Object)instance).name}"); } } } private static void CleanupObjectState(GameObject instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) try { Rigidbody[] componentsInChildren = instance.GetComponentsInChildren<Rigidbody>(true); foreach (Rigidbody val in componentsInChildren) { if ((Object)(object)val != (Object)null && !val.isKinematic) { val.velocity = Vector3.zero; val.angularVelocity = Vector3.zero; val.Sleep(); } } if ((Object)(object)instance.GetComponent<ItemDr