Decompiled source of ValhallaPerformance v1.0.1

Plugins/ValhallaPerformance.dll

Decompiled 2 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValhallaPerformance")]
[assembly: AssemblyDescription("All-in-one Valheim performance optimizer")]
[assembly: AssemblyCompany("Tootsalot")]
[assembly: AssemblyProduct("ValhallaPerformance")]
[assembly: AssemblyCopyright("Tootsalot 2026")]
[assembly: ComVisible(false)]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace ValhallaPerformance;

public static class Cfg
{
	public static ConfigEntry<bool> EnableBoot;

	public static ConfigEntry<bool> EnableSmoke;

	public static ConfigEntry<bool> EnableLights;

	public static ConfigEntry<bool> EnableCulling;

	public static ConfigEntry<bool> EnablePieces;

	public static ConfigEntry<bool> EnableMemory;

	public static ConfigEntry<bool> EnableNetwork;

	public static ConfigEntry<bool> EnableRender;

	public static ConfigEntry<bool> EnableTarPit;

	public static ConfigEntry<bool> EnableLog;

	public static ConfigEntry<bool> SkipIntro;

	public static ConfigEntry<bool> JITWarmup;

	public static ConfigEntry<float> SmokeLift;

	public static ConfigEntry<float> SmokeLife;

	public static ConfigEntry<float> SmokeCullDist;

	public static ConfigEntry<int> SmokeMaxPerSource;

	public static ConfigEntry<bool> SmokeCollision;

	public static ConfigEntry<float> SmokeCollisionRadius;

	public static ConfigEntry<int> MaxLights;

	public static ConfigEntry<float> LightCullDist;

	public static ConfigEntry<float> ShadowCullDist;

	public static ConfigEntry<bool> FreezeFlicker;

	public static ConfigEntry<float> CreatureCullDist;

	public static ConfigEntry<float> PieceCullDist;

	public static ConfigEntry<float> AIThrottleDist;

	public static ConfigEntry<float> AIThrottleInterval;

	public static ConfigEntry<float> LOSCacheDuration;

	public static ConfigEntry<float> SupportCacheTTL;

	public static ConfigEntry<bool> AsyncWearInit;

	public static ConfigEntry<int> AsyncWearBatchSize;

	public static ConfigEntry<int> HeapCeilingMB;

	public static ConfigEntry<float> AssetSweepInterval;

	public static ConfigEntry<bool> GCOnSceneLoad;

	public static ConfigEntry<bool> GCOnPause;

	public static ConfigEntry<bool> PoolItemDrops;

	public static ConfigEntry<bool> PoolAudio;

	public static ConfigEntry<int> ItemPoolSize;

	public static ConfigEntry<int> AudioPoolSize;

	public static ConfigEntry<float> NearRange;

	public static ConfigEntry<float> FarRange;

	public static ConfigEntry<float> FarInterval;

	public static ConfigEntry<bool> PriorityPlayers;

	public static ConfigEntry<bool> SkipUnchanged;

	public static ConfigEntry<int> SendBufferSize;

	public static ConfigEntry<float> LODBias;

	public static ConfigEntry<float> ShadowDist;

	public static ConfigEntry<bool> DisableSoftParticles;

	public static ConfigEntry<bool> DisableSoftVeg;

	public static ConfigEntry<int> ParticleRayBudget;

	public static ConfigEntry<float> GrassDensity;

	public static ConfigEntry<float> DetailDist;

	public static ConfigEntry<bool> MinimapOptimize;

	public static ConfigEntry<float> MinimapUpdateInterval;

	public static ConfigEntry<bool> FrameBudgetGuard;

	public static ConfigEntry<float> FrameBudgetThresholdMs;

	public static ConfigEntry<float> TarCleanupInterval;

	public static ConfigEntry<float> DedupeWindow;

	public static ConfigEntry<int> ReportInterval;

	public static ConfigEntry<bool> MuteHarmony;

	public static ConfigEntry<bool> MuteShaders;

	public static ConfigEntry<LogLevel> MinLogLevel;

	public static void Init(ConfigFile c)
	{
		EnableBoot = c.Bind<bool>("1. Modules", "Boot Optimizer", true, "Skip intro logos, pre-compile hot methods on spawn.");
		EnableSmoke = c.Bind<bool>("1. Modules", "Smoke System", true, "Lightweight smoke physics with simple collision. Replaces Smoke_Collision and VPO smoke.");
		EnableLights = c.Bind<bool>("1. Modules", "Light System", true, "Distance-based light culling, shadow stripping, flicker freeze.");
		EnableCulling = c.Bind<bool>("1. Modules", "Culling System", true, "AI throttling and creature/piece sleep by distance.");
		EnablePieces = c.Bind<bool>("1. Modules", "Piece Optimizer", true, "WearNTear support caching, async initialization.");
		EnableMemory = c.Bind<bool>("1. Modules", "Memory System", true, "GC timing, asset sweeps, object pooling, audio pooling.");
		EnableNetwork = c.Bind<bool>("1. Modules", "Network System", true, "ZDO sync throttling, priority sending, data dedup. Replaces VBNetTweaks.");
		EnableRender = c.Bind<bool>("1. Modules", "Render System", true, "Engine quality tweaks, vegetation, minimap, frame budget guard.");
		EnableTarPit = c.Bind<bool>("1. Modules", "Tar Pit Fix", true, "Cleans up orphaned tar pit VFX that leak memory.");
		EnableLog = c.Bind<bool>("1. Modules", "Log Filter", true, "Deduplicates log spam from large modpacks.");
		SkipIntro = c.Bind<bool>("2. Boot", "Skip Intro", true, "Skip Iron Gate and Coffee Stain logo screens on launch.");
		JITWarmup = c.Bind<bool>("2. Boot", "JIT Warmup", true, "Pre-compile critical methods on first spawn to eliminate first-use stutter.");
		SmokeLift = c.Bind<float>("3. Smoke", "Lift Force", 0.5f, "Upward force on smoke particles. Higher = rises faster.");
		SmokeLife = c.Bind<float>("3. Smoke", "Lifetime", 12f, "Seconds before smoke fades. Vanilla ~15s with heavy physics.");
		SmokeMaxPerSource = c.Bind<int>("3. Smoke", "Max Per Source", 12, "Cap particles per fire source. Vanilla is unlimited.");
		SmokeCullDist = c.Bind<float>("3. Smoke", "Cull Distance", 40f, "Skip smoke physics beyond this distance.");
		SmokeCollision = c.Bind<bool>("3. Smoke", "Enable Collision", true, "Simple raycast collision so smoke doesn't clip through roofs. Replaces Smoke_Collision mod.");
		SmokeCollisionRadius = c.Bind<float>("3. Smoke", "Collision Check Radius", 0.3f, "Radius of the upward raycast for roof detection.");
		MaxLights = c.Bind<int>("4. Lights", "Max Active Lights", 20, "Maximum simultaneous dynamic lights. Excess disabled by distance.");
		LightCullDist = c.Bind<float>("4. Lights", "Cull Distance", 55f, "Lights beyond this distance are fully disabled.");
		ShadowCullDist = c.Bind<float>("4. Lights", "Shadow Cull Distance", 35f, "Shadows disabled beyond this. Light still renders, just flat.");
		FreezeFlicker = c.Bind<bool>("4. Lights", "Freeze Flicker", true, "Fix fire/torch intensity at base value. Stops per-frame shadow map rebuilds.\nBiggest GPU win on large bases. Fires look slightly static.");
		CreatureCullDist = c.Bind<float>("5. Culling", "Creature Sleep Distance", 80f, "Creatures beyond this pause their Update logic.");
		PieceCullDist = c.Bind<float>("5. Culling", "Piece Sleep Distance", 100f, "Building pieces beyond this skip Update entirely.");
		AIThrottleDist = c.Bind<float>("5. Culling", "AI Throttle Distance", 60f, "Monsters beyond this update AI at reduced frequency.");
		AIThrottleInterval = c.Bind<float>("5. Culling", "AI Throttle Interval", 4f, "Seconds between AI updates for distant monsters.");
		LOSCacheDuration = c.Bind<float>("5. Culling", "LOS Cache Duration", 0.5f, "Seconds to cache line-of-sight checks per target.");
		SupportCacheTTL = c.Bind<float>("6. Pieces", "Support Cache TTL", 5f, "Seconds to cache WearNTear.GetSupport results.");
		AsyncWearInit = c.Bind<bool>("6. Pieces", "Async WearNTear Init", true, "Spread WearNTear initialization across multiple frames on scene load.");
		AsyncWearBatchSize = c.Bind<int>("6. Pieces", "Async Batch Size", 20, "WearNTear components initialized per frame during async init.");
		HeapCeilingMB = c.Bind<int>("7. Memory", "Heap Ceiling MB", 2048, "Trigger cleanup when heap exceeds this. 0 to disable.");
		AssetSweepInterval = c.Bind<float>("7. Memory", "Asset Sweep Interval", 300f, "Seconds between unused asset unloads. 0 to disable.");
		GCOnSceneLoad = c.Bind<bool>("7. Memory", "GC On Scene Load", true, "");
		GCOnPause = c.Bind<bool>("7. Memory", "GC On Pause", true, "");
		PoolItemDrops = c.Bind<bool>("7. Memory", "Pool Item Drops", true, "Reuse item drop GameObjects instead of creating/destroying.");
		PoolAudio = c.Bind<bool>("7. Memory", "Pool Audio Sources", true, "Reuse AudioSource components instead of creating new ones per sound.");
		ItemPoolSize = c.Bind<int>("7. Memory", "Item Pool Size", 64, "");
		AudioPoolSize = c.Bind<int>("7. Memory", "Audio Pool Size", 32, "");
		NearRange = c.Bind<float>("8. Network", "Near Range", 64f, "Full sync rate within this distance.");
		FarRange = c.Bind<float>("8. Network", "Far Range", 128f, "Reduced sync rate beyond this distance.");
		FarInterval = c.Bind<float>("8. Network", "Far Sync Interval", 4f, "Seconds between sync for distant objects.");
		PriorityPlayers = c.Bind<bool>("8. Network", "Prioritize Players", true, "Always sync player ZDOs at full rate.");
		SkipUnchanged = c.Bind<bool>("8. Network", "Skip Unchanged", true, "Skip sending ZDOs whose data revision hasn't changed.");
		SendBufferSize = c.Bind<int>("8. Network", "Send Buffer Size", 16384, "Network send buffer size in bytes. Higher = more data per packet, fewer packets.");
		LODBias = c.Bind<float>("9. Render", "LOD Bias", 1.2f, "LOD transition multiplier. 1.0=vanilla, higher=low-poly sooner at distance.");
		ShadowDist = c.Bind<float>("9. Render", "Shadow Distance", 80f, "Maximum shadow render distance.");
		DisableSoftParticles = c.Bind<bool>("9. Render", "Disable Soft Particles", true, "Skip per-pixel depth reads on particle edges.");
		DisableSoftVeg = c.Bind<bool>("9. Render", "Disable Soft Vegetation", true, "Skip extra GPU pass for grass/bush border blending.");
		ParticleRayBudget = c.Bind<int>("9. Render", "Particle Raycast Budget", 1024, "Max particle collision raycasts per frame. Vanilla is 4096.");
		GrassDensity = c.Bind<float>("9. Render", "Grass Density", 0.75f, "Grass density multiplier. 1.0=vanilla, lower=fewer blades.");
		DetailDist = c.Bind<float>("9. Render", "Detail Distance", 80f, "Distance at which detail objects (grass, small rocks) are visible.");
		MinimapOptimize = c.Bind<bool>("9. Render", "Minimap Optimize", true, "Reduce minimap update frequency.");
		MinimapUpdateInterval = c.Bind<float>("9. Render", "Minimap Update Interval", 0.5f, "Seconds between minimap texture updates. Vanilla is every frame.");
		FrameBudgetGuard = c.Bind<bool>("9. Render", "Frame Budget Guard", true, "Prevents physics death spiral during heavy load spikes.\nConverts hard freezes into brief slow-motion.");
		FrameBudgetThresholdMs = c.Bind<float>("9. Render", "Frame Budget Threshold", 28f, "1% low frametime in ms that triggers the frame budget guard.");
		TarCleanupInterval = c.Bind<float>("10. Tar Pit", "Cleanup Interval", 30f, "Seconds between orphaned VFX scans.");
		DedupeWindow = c.Bind<float>("11. Log", "Dedupe Window", 5f, "");
		ReportInterval = c.Bind<int>("11. Log", "Report Interval", 60, "");
		MuteHarmony = c.Bind<bool>("11. Log", "Mute Harmony", true, "Suppress Harmony patch logs during boot.");
		MuteShaders = c.Bind<bool>("11. Log", "Mute Shader Warnings", true, "");
		MinLogLevel = c.Bind<LogLevel>("11. Log", "Min Log Level", (LogLevel)16, "");
	}
}
[BepInPlugin("tootsalot.ValhallaPerformance", "ValhallaPerformance", "1.0.0")]
[BepInProcess("valheim.exe")]
public class Plugin : BaseUnityPlugin
{
	public const string GUID = "tootsalot.ValhallaPerformance";

	public const string Name = "ValhallaPerformance";

	public const string Ver = "1.0.0";

	internal static Plugin Instance;

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private ISystem[] _systems;

	private void Awake()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		Cfg.Init(((BaseUnityPlugin)this).Config);
		_harmony = new Harmony("tootsalot.ValhallaPerformance");
		_systems = new ISystem[10]
		{
			Cfg.EnableBoot.Value ? new BootSystem() : null,
			Cfg.EnableLog.Value ? new LogSystem() : null,
			Cfg.EnableSmoke.Value ? new SmokeSystem() : null,
			Cfg.EnableLights.Value ? new LightSystem() : null,
			Cfg.EnableRender.Value ? new RenderSystem() : null,
			Cfg.EnableCulling.Value ? new CullingSystem() : null,
			Cfg.EnablePieces.Value ? new PieceSystem() : null,
			Cfg.EnableTarPit.Value ? new TarPitSystem() : null,
			Cfg.EnableMemory.Value ? new MemorySystem() : null,
			Cfg.EnableNetwork.Value ? new NetworkSystem() : null
		};
		int num = 0;
		ISystem[] systems = _systems;
		foreach (ISystem system in systems)
		{
			if (system != null)
			{
				system.Init(_harmony);
				num++;
			}
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} — {2} systems active", "ValhallaPerformance", "1.0.0", num));
	}

	private void Update()
	{
		ISystem[] systems = _systems;
		for (int i = 0; i < systems.Length; i++)
		{
			systems[i]?.Tick();
		}
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
		ISystem[] systems = _systems;
		for (int i = 0; i < systems.Length; i++)
		{
			systems[i]?.Cleanup();
		}
	}
}
public interface ISystem
{
	void Init(Harmony harmony);

	void Tick();

	void Cleanup();
}
public class BootSystem : ISystem
{
	[HarmonyPatch]
	private static class IntroPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "Awake")]
		private static void Postfix_Awake(FejdStartup __instance)
		{
			try
			{
				Traverse.Create((object)__instance).Field("m_introLogosDone").SetValue((object)true);
			}
			catch
			{
				try
				{
					Traverse.Create((object)__instance).Field("m_introDone").SetValue((object)true);
				}
				catch
				{
				}
			}
		}
	}

	private bool _warmupDone;

	private float _warmupDelay = 5f;

	public void Init(Harmony harmony)
	{
		if (Cfg.SkipIntro.Value)
		{
			harmony.PatchAll(typeof(IntroPatches));
		}
		Plugin.Log.LogInfo((object)"[Boot] Active");
	}

	public void Tick()
	{
		if (!_warmupDone && Cfg.JITWarmup.Value && !((Object)(object)Player.m_localPlayer == (Object)null))
		{
			_warmupDelay -= Time.unscaledDeltaTime;
			if (!(_warmupDelay > 0f))
			{
				_warmupDone = true;
				DoJITWarmup();
			}
		}
	}

	public void Cleanup()
	{
	}

	private void DoJITWarmup()
	{
		Type[] obj = new Type[11]
		{
			typeof(Player),
			typeof(Character),
			typeof(Humanoid),
			typeof(ItemDrop),
			typeof(Inventory),
			typeof(ZDOMan),
			typeof(WearNTear),
			typeof(Piece),
			typeof(Minimap),
			typeof(MonsterAI),
			typeof(BaseAI)
		};
		int num = 0;
		Type[] array = obj;
		foreach (Type type in array)
		{
			try
			{
				MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!methodInfo.IsAbstract && !methodInfo.IsGenericMethod && methodInfo.GetMethodBody() != null)
					{
						try
						{
							RuntimeHelpers.PrepareMethod(methodInfo.MethodHandle);
							num++;
						}
						catch
						{
						}
					}
				}
			}
			catch
			{
			}
		}
		Plugin.Log.LogInfo((object)$"[Boot] JIT warmup: pre-compiled {num} methods");
	}
}
public class CullingSystem : ISystem
{
	[HarmonyPatch]
	private static class Patches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static void Prefix_UpdateAI(MonsterAI __instance, float dt)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Vector3 val = ((Component)__instance).transform.position - _playerPos;
			float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
			float num = Cfg.AIThrottleDist.Value * Cfg.AIThrottleDist.Value;
			if (!(sqrMagnitude <= num))
			{
				float value = Cfg.AIThrottleInterval.Value;
				float num2 = (float)(((Object)__instance).GetInstanceID() % 100) * 0.01f * value;
				if ((Time.time + num2) % value >= dt)
				{
					((Behaviour)__instance).enabled = false;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static void Postfix_UpdateAI(MonsterAI __instance)
		{
			if ((Object)(object)__instance != (Object)null && !((Behaviour)__instance).enabled)
			{
				((Behaviour)__instance).enabled = true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		private static void Prefix_WearNTear(WearNTear __instance)
		{
			//IL_001d: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				Vector3 val = ((Component)__instance).transform.position - ((Component)Player.m_localPlayer).transform.position;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				float num = Cfg.PieceCullDist.Value * Cfg.PieceCullDist.Value;
				if (sqrMagnitude > num)
				{
					((Behaviour)__instance).enabled = false;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		private static void Postfix_WearNTear(WearNTear __instance)
		{
			if ((Object)(object)__instance != (Object)null && !((Behaviour)__instance).enabled)
			{
				((Behaviour)__instance).enabled = true;
			}
		}
	}

	internal static class LOSPatches
	{
		public static void Postfix_HavePath(BaseAI __instance, Vector3 target, bool __result)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				CacheLOS(((Object)__instance).GetInstanceID(), ((object)(Vector3)(ref target)).GetHashCode(), __result);
			}
		}
	}

	private static Vector3 _playerPos;

	private float _lastCacheClean;

	private static readonly Dictionary<long, (bool result, float time)> _losCache = new Dictionary<long, (bool, float)>();

	public void Init(Harmony harmony)
	{
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Expected O, but got Unknown
		harmony.PatchAll(typeof(Patches));
		MethodInfo methodInfo = AccessTools.Method(typeof(BaseAI), "HavePath", (Type[])null, (Type[])null);
		if (methodInfo != null)
		{
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(LOSPatches), "Postfix_HavePath", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}
		Plugin.Log.LogInfo((object)"[Culling] Active");
	}

	public void Tick()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			_playerPos = ((Component)Player.m_localPlayer).transform.position;
		}
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime - _lastCacheClean > 10f)
		{
			_lastCacheClean = unscaledTime;
			PurgeLOSCache(unscaledTime);
		}
	}

	public void Cleanup()
	{
		_losCache.Clear();
	}

	private void PurgeLOSCache(float time)
	{
		float num = Cfg.LOSCacheDuration.Value * 5f;
		List<long> list = new List<long>();
		foreach (KeyValuePair<long, (bool, float)> item in _losCache)
		{
			if (time - item.Value.Item2 > num)
			{
				list.Add(item.Key);
			}
		}
		foreach (long item2 in list)
		{
			_losCache.Remove(item2);
		}
	}

	internal static void CacheLOS(int a, int b, bool result)
	{
		long key = ((long)a << 32) | (uint)b;
		_losCache[key] = (result, Time.unscaledTime);
	}
}
public class LightSystem : ISystem
{
	[HarmonyPatch]
	private static class FlickerPatch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(LightFlicker), "CustomUpdate")]
		private static void Prefix(LightFlicker __instance)
		{
			Traverse obj = Traverse.Create((object)__instance);
			Light value = obj.Field("m_light").GetValue<Light>();
			float value2 = obj.Field("m_baseIntensity").GetValue<float>();
			if ((Object)(object)value != (Object)null)
			{
				value.intensity = value2;
			}
		}
	}

	private static Vector3 _playerPos;

	private float _lastUpdate;

	public void Init(Harmony harmony)
	{
		if (Cfg.FreezeFlicker.Value)
		{
			harmony.PatchAll(typeof(FlickerPatch));
		}
		Plugin.Log.LogInfo((object)"[Lights] Active");
	}

	public void Tick()
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		float unscaledTime = Time.unscaledTime;
		if (!(unscaledTime - _lastUpdate < 0.5f))
		{
			_lastUpdate = unscaledTime;
			if (!((Object)(object)Player.m_localPlayer == (Object)null))
			{
				_playerPos = ((Component)Player.m_localPlayer).transform.position;
				ManageLights();
			}
		}
	}

	public void Cleanup()
	{
	}

	private void ManageLights()
	{
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Invalid comparison between Unknown and I4
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		int value = Cfg.MaxLights.Value;
		float num = Cfg.LightCullDist.Value * Cfg.LightCullDist.Value;
		float num2 = Cfg.ShadowCullDist.Value * Cfg.ShadowCullDist.Value;
		Light[] array = Object.FindObjectsByType<Light>((FindObjectsSortMode)0);
		Array.Sort(array, delegate(Light a, Light b)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)a == (Object)null || (Object)(object)b == (Object)null)
			{
				return 0;
			}
			Vector3 val3 = ((Component)a).transform.position - _playerPos;
			float sqrMagnitude2 = ((Vector3)(ref val3)).sqrMagnitude;
			val3 = ((Component)b).transform.position - _playerPos;
			return sqrMagnitude2.CompareTo(((Vector3)(ref val3)).sqrMagnitude);
		});
		int num3 = 0;
		Light[] array2 = array;
		foreach (Light val in array2)
		{
			if ((Object)(object)val == (Object)null || (int)val.type == 1)
			{
				continue;
			}
			Vector3 val2 = ((Component)val).transform.position - _playerPos;
			float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
			if (sqrMagnitude > num || num3 >= value)
			{
				if (((Behaviour)val).enabled)
				{
					((Behaviour)val).enabled = false;
				}
				continue;
			}
			if (!((Behaviour)val).enabled)
			{
				((Behaviour)val).enabled = true;
			}
			if (sqrMagnitude > num2 && (int)val.shadows != 0)
			{
				val.shadows = (LightShadows)0;
			}
			num3++;
		}
	}
}
public class LogSystem : ISystem
{
	[HarmonyPatch]
	private static class Patch
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(ManualLogSource), "Log")]
		private static bool Prefix(LogLevel level, object data, ManualLogSource __instance)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (__instance.SourceName == "ValhallaPerformance")
			{
				return true;
			}
			return !ShouldDrop(level, data);
		}
	}

	private static readonly Dictionary<int, (float time, int count)> _seen = new Dictionary<int, (float, int)>();

	private static int _suppressed;

	private static float _lastReport;

	private static float _lastPurge;

	private static readonly string[] Noise = new string[12]
	{
		"shader is not supported", "Can't find shader", "Missing asset bundle", "material doesn't have", "The referenced script", "Setting quality level", "Shader wants texture", "%.teleport_log", "%.RPC_", "%.DamageText",
		"%.FlashColor", "%.AddNoise"
	};

	public void Init(Harmony harmony)
	{
		harmony.PatchAll(typeof(Patch));
		Plugin.Log.LogInfo((object)"[Log] Filter active");
	}

	public void Tick()
	{
		float unscaledTime = Time.unscaledTime;
		if (_suppressed > 0 && unscaledTime - _lastReport > (float)Cfg.ReportInterval.Value)
		{
			_lastReport = unscaledTime;
			Plugin.Log.LogInfo((object)$"[Log] Suppressed {_suppressed} noise messages");
			_suppressed = 0;
		}
		if (!(unscaledTime - _lastPurge > 30f))
		{
			return;
		}
		_lastPurge = unscaledTime;
		float num = Cfg.DedupeWindow.Value * 4f;
		List<int> list = new List<int>();
		foreach (KeyValuePair<int, (float, int)> item in _seen)
		{
			if (unscaledTime - item.Value.Item1 > num)
			{
				list.Add(item.Key);
			}
		}
		foreach (int item2 in list)
		{
			_seen.Remove(item2);
		}
	}

	public void Cleanup()
	{
		_seen.Clear();
	}

	internal static bool ShouldDrop(LogLevel level, object data)
	{
		//IL_0000: 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)
		if (level < Cfg.MinLogLevel.Value)
		{
			_suppressed++;
			return true;
		}
		if (data == null)
		{
			return false;
		}
		string text = data.ToString();
		if (string.IsNullOrEmpty(text))
		{
			return false;
		}
		if (Cfg.MuteHarmony.Value && (text.StartsWith("Patching ", StringComparison.Ordinal) || text.Contains("Harmony id=") || text.Contains("[HarmonyLib")))
		{
			_suppressed++;
			return true;
		}
		if (Cfg.MuteShaders.Value)
		{
			for (int i = 0; i < Noise.Length; i++)
			{
				if (text.IndexOf(Noise[i], StringComparison.OrdinalIgnoreCase) >= 0)
				{
					_suppressed++;
					return true;
				}
			}
		}
		int hashCode = text.GetHashCode();
		float unscaledTime = Time.unscaledTime;
		if (_seen.TryGetValue(hashCode, out (float, int) value) && unscaledTime - value.Item1 < Cfg.DedupeWindow.Value)
		{
			_seen[hashCode] = (value.Item1, value.Item2 + 1);
			_suppressed++;
			return true;
		}
		_seen[hashCode] = (unscaledTime, 1);
		return false;
	}
}
public class MemorySystem : ISystem
{
	[HarmonyPatch]
	private static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Menu), "Show")]
		private static void OnPause()
		{
			if (Cfg.GCOnPause.Value)
			{
				GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, blocking: false);
				GC.WaitForPendingFinalizers();
			}
		}
	}

	private float _lastSweep;

	private float _lastHeapCheck;

	private static bool _sceneLoadPending;

	private static readonly Queue<AudioSource> _audioPool = new Queue<AudioSource>();

	public void Init(Harmony harmony)
	{
		harmony.PatchAll(typeof(Patches));
		SceneManager.sceneLoaded += delegate
		{
			_sceneLoadPending = true;
		};
		Plugin.Log.LogInfo((object)"[Memory] Active");
	}

	public void Tick()
	{
		float unscaledTime = Time.unscaledTime;
		if (_sceneLoadPending)
		{
			_sceneLoadPending = false;
			if (Cfg.GCOnSceneLoad.Value)
			{
				DoGC("scene load");
			}
		}
		float value = Cfg.AssetSweepInterval.Value;
		if (value > 0f && unscaledTime - _lastSweep > value)
		{
			_lastSweep = unscaledTime;
			if (IsSafe())
			{
				Resources.UnloadUnusedAssets();
			}
		}
		if (!(unscaledTime - _lastHeapCheck > 15f))
		{
			return;
		}
		_lastHeapCheck = unscaledTime;
		int value2 = Cfg.HeapCeilingMB.Value;
		if (value2 > 0)
		{
			long num = GC.GetTotalMemory(forceFullCollection: false) / 1048576;
			if (num > value2)
			{
				DoGC($"heap {num}MB>{value2}MB");
			}
		}
	}

	public void Cleanup()
	{
		_audioPool.Clear();
	}

	private bool IsSafe()
	{
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return true;
		}
		try
		{
			return !Traverse.Create((object)localPlayer).Method("InCombat", Array.Empty<object>()).GetValue<bool>() && !((Character)localPlayer).IsSwimming();
		}
		catch
		{
			return !((Character)localPlayer).IsSwimming();
		}
	}

	private void DoGC(string reason)
	{
		long num = GC.GetTotalMemory(forceFullCollection: false) / 1048576;
		GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, blocking: false);
		GC.WaitForPendingFinalizers();
		Resources.UnloadUnusedAssets();
		long num2 = GC.GetTotalMemory(forceFullCollection: false) / 1048576;
		if (num - num2 > 5)
		{
			Plugin.Log.LogInfo((object)$"[Memory] GC ({reason}): {num}->{num2}MB");
		}
	}
}
public class NetworkSystem : ISystem
{
	[HarmonyPatch]
	private static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZDOMan), "Update")]
		private static void Postfix_ZDOMan()
		{
		}
	}

	private static readonly Dictionary<ZDOID, float> _lastSend = new Dictionary<ZDOID, float>();

	private static readonly Dictionary<ZDOID, uint> _lastRevision = new Dictionary<ZDOID, uint>();

	private static Vector3 _playerPos;

	private float _lastClean;

	private float _lastPosUpdate;

	private float _lastReport;

	private static int _sent;

	private static int _skipped;

	public void Init(Harmony harmony)
	{
		harmony.PatchAll(typeof(Patches));
		Plugin.Log.LogInfo((object)"[Network] Active");
	}

	public void Tick()
	{
		//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)
		float unscaledTime = Time.unscaledTime;
		if (unscaledTime - _lastPosUpdate > 0.5f)
		{
			_lastPosUpdate = unscaledTime;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				_playerPos = ((Component)Player.m_localPlayer).transform.position;
			}
		}
		if (unscaledTime - _lastClean > 60f)
		{
			_lastClean = unscaledTime;
			PurgeCaches(unscaledTime);
		}
		int num = _sent + _skipped;
		if (num > 0 && unscaledTime - _lastReport > 300f)
		{
			_lastReport = unscaledTime;
			Plugin.Log.LogInfo((object)$"[Network] Sync: {_sent} sent, {_skipped} skipped ({(float)_skipped * 100f / (float)num:F0}% saved)");
			_sent = (_skipped = 0);
		}
	}

	public void Cleanup()
	{
		_lastSend.Clear();
		_lastRevision.Clear();
	}

	internal static bool AllowSync(ZDO zdo)
	{
		//IL_003e: 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)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0099: Unknown result type (might be due to invalid IL or missing references)
		//IL_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		if (zdo == null || !zdo.IsValid() || zdo.IsOwner())
		{
			return true;
		}
		if (Cfg.PriorityPlayers.Value && zdo.GetInt(ZDOVars.s_playerID, 0) != 0)
		{
			_sent++;
			return true;
		}
		Vector3 val = zdo.GetPosition() - _playerPos;
		float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
		float num = Cfg.NearRange.Value * Cfg.NearRange.Value;
		float num2 = Cfg.FarRange.Value * Cfg.FarRange.Value;
		if (sqrMagnitude < num)
		{
			_sent++;
			return true;
		}
		if (sqrMagnitude > num2)
		{
			ZDOID uid = zdo.m_uid;
			float unscaledTime = Time.unscaledTime;
			if (_lastSend.TryGetValue(uid, out var value) && unscaledTime - value < Cfg.FarInterval.Value)
			{
				_skipped++;
				return false;
			}
			_lastSend[uid] = unscaledTime;
		}
		if (Cfg.SkipUnchanged.Value)
		{
			ZDOID uid2 = zdo.m_uid;
			uint dataRevision = zdo.DataRevision;
			if (_lastRevision.TryGetValue(uid2, out var value2) && dataRevision == value2)
			{
				_skipped++;
				return false;
			}
			_lastRevision[uid2] = dataRevision;
		}
		_sent++;
		return true;
	}

	private void PurgeCaches(float time)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		float num = Cfg.FarInterval.Value * 10f;
		List<ZDOID> list = new List<ZDOID>();
		foreach (KeyValuePair<ZDOID, float> item in _lastSend)
		{
			if (time - item.Value > num)
			{
				list.Add(item.Key);
			}
		}
		foreach (ZDOID item2 in list)
		{
			_lastSend.Remove(item2);
			_lastRevision.Remove(item2);
		}
	}
}
public class PieceSystem : ISystem
{
	[HarmonyPatch]
	private static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		private static void Postfix_UpdateWear(WearNTear __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				int instanceID = ((Object)__instance).GetInstanceID();
				float value = Traverse.Create((object)__instance).Field("m_support").GetValue<float>();
				_cache[instanceID] = (value, Time.unscaledTime);
			}
		}
	}

	private static readonly Dictionary<int, (float val, float time)> _cache = new Dictionary<int, (float, float)>();

	private float _lastPurge;

	public void Init(Harmony harmony)
	{
		harmony.PatchAll(typeof(Patches));
		Plugin.Log.LogInfo((object)"[Pieces] Active — support caching");
	}

	public void Tick()
	{
		float unscaledTime = Time.unscaledTime;
		if (!(unscaledTime - _lastPurge > 15f))
		{
			return;
		}
		_lastPurge = unscaledTime;
		float num = Cfg.SupportCacheTTL.Value * 3f;
		List<int> list = new List<int>();
		foreach (KeyValuePair<int, (float, float)> item in _cache)
		{
			if (unscaledTime - item.Value.Item2 > num)
			{
				list.Add(item.Key);
			}
		}
		foreach (int item2 in list)
		{
			_cache.Remove(item2);
		}
	}

	public void Cleanup()
	{
		_cache.Clear();
	}
}
public class RenderSystem : ISystem
{
	private bool _tweaksApplied;

	private readonly float[] _frameTimes = new float[120];

	private int _frameIndex;

	private float _savedMaxDelta;

	private bool _guardActive;

	public void Init(Harmony harmony)
	{
		Plugin.Log.LogInfo((object)"[Render] Active");
	}

	public void Tick()
	{
		if (!_tweaksApplied)
		{
			ApplyEngineTweaks();
			_tweaksApplied = true;
		}
		if (Cfg.FrameBudgetGuard.Value)
		{
			UpdateFrameBudgetGuard();
		}
	}

	public void Cleanup()
	{
		if (_guardActive && _savedMaxDelta > 0f)
		{
			Time.maximumDeltaTime = _savedMaxDelta;
		}
	}

	private void ApplyEngineTweaks()
	{
		QualitySettings.lodBias = Cfg.LODBias.Value;
		if (Cfg.DisableSoftParticles.Value)
		{
			QualitySettings.softParticles = false;
		}
		if (Cfg.DisableSoftVeg.Value)
		{
			QualitySettings.softVegetation = false;
		}
		QualitySettings.particleRaycastBudget = Cfg.ParticleRayBudget.Value;
		QualitySettings.shadowDistance = Cfg.ShadowDist.Value;
		Plugin.Log.LogInfo((object)($"[Render] LOD={Cfg.LODBias.Value:F1} softP={QualitySettings.softParticles} " + $"softV={QualitySettings.softVegetation} pRay={Cfg.ParticleRayBudget.Value} shadow={Cfg.ShadowDist.Value}m"));
	}

	private void UpdateFrameBudgetGuard()
	{
		_frameTimes[_frameIndex] = Time.unscaledDeltaTime * 1000f;
		_frameIndex = (_frameIndex + 1) % _frameTimes.Length;
		if (_frameIndex % 60 == 0)
		{
			float[] array = new float[_frameTimes.Length];
			Array.Copy(_frameTimes, array, _frameTimes.Length);
			Array.Sort(array);
			int num = Mathf.Max(1, array.Length / 100);
			float num2 = 0f;
			for (int i = array.Length - num; i < array.Length; i++)
			{
				num2 += array[i];
			}
			num2 /= (float)num;
			float value = Cfg.FrameBudgetThresholdMs.Value;
			if (num2 > value && !_guardActive)
			{
				_savedMaxDelta = Time.maximumDeltaTime;
				Time.maximumDeltaTime = 0.04f;
				_guardActive = true;
			}
			else if (num2 < value * 0.7f && _guardActive)
			{
				Time.maximumDeltaTime = _savedMaxDelta;
				_guardActive = false;
			}
		}
	}
}
public class SmokeSystem : ISystem
{
	[HarmonyPatch]
	private static class Patches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(Smoke), "CustomUpdate")]
		private static void Prefix_CustomUpdate(Smoke __instance, float deltaTime, float time)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			Traverse val = Traverse.Create((object)__instance);
			float value = val.Field("m_time").GetValue<float>();
			value += deltaTime;
			val.Field("m_time").SetValue((object)value);
			float value2 = Cfg.SmokeLife.Value;
			if (value > value2)
			{
				Object.Destroy((Object)(object)((Component)__instance).gameObject);
				return;
			}
			RefreshPos();
			Vector3 position = ((Component)__instance).transform.position;
			float num = Cfg.SmokeCullDist.Value * Cfg.SmokeCullDist.Value;
			Vector3 val2 = position - _playerPos;
			if (((Vector3)(ref val2)).sqrMagnitude > num)
			{
				return;
			}
			float num2 = value / value2;
			float num3 = Cfg.SmokeLift.Value * (1f - num2 * num2);
			float num4 = (float)((Object)__instance).GetInstanceID() * 0.137f;
			float num5 = Mathf.Sin(Time.time * 0.3f + num4) * 0.12f;
			float num6 = Mathf.Cos(Time.time * 0.23f + num4 + 2.1f) * 0.12f;
			Vector3 val3 = default(Vector3);
			((Vector3)(ref val3))..ctor(num5, num3, num6);
			if (Cfg.SmokeCollision.Value && num3 > 0.01f)
			{
				float num7 = num3 * deltaTime * 3f + 0.5f;
				if (Physics.SphereCast(position, Cfg.SmokeCollisionRadius.Value, Vector3.up, ref _hitInfo, num7, LayerMask.GetMask(new string[4] { "piece", "static_solid", "Default_small", "terrain" })))
				{
					val3.y = Mathf.Min(val3.y, 0.01f);
					val3.x *= 3f;
					val3.z *= 3f;
					if (((RaycastHit)(ref _hitInfo)).distance < 0.3f)
					{
						val3.x *= 0.5f;
						val3.z *= 0.5f;
					}
				}
			}
			Transform transform = ((Component)__instance).transform;
			transform.position += val3 * deltaTime;
			float num8 = ((num2 < 0.25f) ? 1f : (1f - (num2 - 0.25f) / 0.75f));
			MeshRenderer value3 = val.Field("m_renderer").GetValue<MeshRenderer>();
			if ((Object)(object)value3 != (Object)null && (Object)(object)((Renderer)value3).material != (Object)null)
			{
				Color color = ((Renderer)value3).material.color;
				color.a = Mathf.Clamp01(num8);
				((Renderer)value3).material.color = color;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Smoke), "Awake")]
		private static bool Prefix_Awake(Smoke __instance)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			int key = AreaHash(((Component)__instance).transform.position);
			_areaCount.TryGetValue(key, out var value);
			if (value >= Cfg.SmokeMaxPerSource.Value)
			{
				Object.Destroy((Object)(object)((Component)__instance).gameObject);
				return false;
			}
			_areaCount[key] = value + 1;
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Smoke), "OnDestroy")]
		private static void Postfix_OnDestroy(Smoke __instance)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int key = AreaHash(((Component)__instance).transform.position);
			if (_areaCount.TryGetValue(key, out var value))
			{
				if (--value <= 0)
				{
					_areaCount.Remove(key);
				}
				else
				{
					_areaCount[key] = value;
				}
			}
		}
	}

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

	private static Vector3 _playerPos;

	private static float _lastPosUpdate;

	private static RaycastHit _hitInfo;

	public void Init(Harmony harmony)
	{
		harmony.PatchAll(typeof(Patches));
		Plugin.Log.LogInfo((object)"[Smoke] Active");
	}

	public void Tick()
	{
	}

	public void Cleanup()
	{
		_areaCount.Clear();
	}

	private static void RefreshPos()
	{
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		if (!(Time.unscaledTime - _lastPosUpdate < 1f))
		{
			_lastPosUpdate = Time.unscaledTime;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				_playerPos = ((Component)Player.m_localPlayer).transform.position;
			}
		}
	}

	private static int AreaHash(Vector3 p)
	{
		//IL_0000: 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_002f: Unknown result type (might be due to invalid IL or missing references)
		return (Mathf.FloorToInt(p.x * 0.5f) * 73856093) ^ (Mathf.FloorToInt(p.y * 0.5f) * 19349663) ^ (Mathf.FloorToInt(p.z * 0.5f) * 83492791);
	}
}
public class TarPitSystem : ISystem
{
	private float _lastScan;

	private static readonly string[] TarVfxNames = new string[4] { "vfx_tar", "fx_tar", "TarBubble", "TarSplash" };

	public void Init(Harmony harmony)
	{
		Plugin.Log.LogInfo((object)"[TarPit] VFX leak fix active");
	}

	public void Tick()
	{
		float time = Time.time;
		if (time - _lastScan < Cfg.TarCleanupInterval.Value)
		{
			return;
		}
		_lastScan = time;
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		ParticleSystem[] array = Object.FindObjectsByType<ParticleSystem>((FindObjectsSortMode)0);
		int num = 0;
		ParticleSystem[] array2 = array;
		foreach (ParticleSystem val in array2)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			GameObject gameObject = ((Component)val).gameObject;
			if ((Object)(object)gameObject == (Object)null)
			{
				continue;
			}
			string name = ((Object)gameObject).name;
			bool flag = false;
			for (int j = 0; j < TarVfxNames.Length; j++)
			{
				if (name.IndexOf(TarVfxNames[j], StringComparison.OrdinalIgnoreCase) >= 0)
				{
					flag = true;
					break;
				}
			}
			if (flag && (((Object)(object)gameObject.transform.parent == (Object)null && !val.isPlaying && val.particleCount == 0) || (!gameObject.activeInHierarchy && !val.isPlaying) || (val.isStopped && val.particleCount == 0 && val.time > 5f)))
			{
				Object.Destroy((Object)(object)gameObject);
				num++;
			}
		}
		if (num > 0)
		{
			Plugin.Log.LogInfo((object)$"[TarPit] Cleaned {num} orphaned VFX");
		}
	}

	public void Cleanup()
	{
	}
}