Decompiled source of HitchFix v2.0.0

plugins/ValheimPerformanceFixes.dll

Decompiled 20 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using DropNSpawnCache;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MinimapCache;
using RenderTrim;
using Riverheim;
using Riverheim.Pipeline;
using Riverheim.Rendering;
using Riverheim.Util.Random;
using Riverheim.World;
using RiverheimCache;
using SoftReferenceableAssets;
using SpawnBudget;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Rendering;
using UnityEngine.Scripting;
using ValheimPerformanceFixes;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimPerformanceFixes")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("2.0.0.0")]
[assembly: AssemblyInformationalVersion("2.0.0")]
[assembly: AssemblyProduct("ValheimPerformanceFixes")]
[assembly: AssemblyTitle("ValheimPerformanceFixes")]
[assembly: AssemblyVersion("2.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public static class SpawnBudgetPressure
{
	public static float Value;

	public static float Raw;

	public static float BudgetMs;

	public static int Instances;

	public static int PendingNear;

	public static int PendingDistant;

	public static bool Combat;
}
namespace RiverheimCache
{
	public class Plugin : ModuleBehaviour
	{
		internal static ManualLogSource L;

		private const int FORMAT = 1;

		private const string MAGIC = "RHCACHE1";

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> VerifyFirstLoad;

		private static ConfigEntry<bool> VerifyEveryLoad;

		private static ConfigEntry<int> SamplePoints;

		private static volatile WorldState capWs;

		private static volatile HeightMap capHm;

		private static string curKey;

		private static string curPath;

		private static int curSeed;

		private static bool curVerifyRun;

		private static byte[] curLoadedSha;

		private static WorldRenderer curLoadedRenderer;

		private void Awake()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_0113: Expected O, but got Unknown
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			L = base.Logger;
			Enabled = base.Config.Bind<bool>("Optional - Riverheim Cache - General", "Enabled", true, "Cache Riverheim worldgen results on disk (BepInEx/cache/RiverheimCache). Disable to always compute normally.");
			VerifyFirstLoad = base.Config.Bind<bool>("Optional - Riverheim Cache - Safety", "VerifyFirstLoad", true, "The first time a newly written cache file would be used, also compute normally and require the computed data to be byte-identical to the cache (proves determinism). The computed result is used for that join. Afterwards the cache is trusted (still SHA256-checked on every load).");
			VerifyEveryLoad = base.Config.Bind<bool>("Optional - Riverheim Cache - Safety", "VerifyEveryLoad", false, "Debug: verify against a full computation on every join (no time saving).");
			SamplePoints = base.Config.Bind<int>("Optional - Riverheim Cache - Safety", "SamplePoints", 4096, "Number of world points sampled to compare the cache-built renderer against the computed one when a cache is written/verified.");
			SeedFromPluginFolder();
			try
			{
				Harmony val = new Harmony("vpf.ValheimPerformanceFixes.RiverheimCache");
				val.Patch((MethodBase)AccessTools.Method(typeof(GeneratorPipeline), "Compute", new Type[2]
				{
					typeof(int),
					typeof(object)
				}, (Type[])null), new HarmonyMethod(typeof(Plugin), "ComputePre", (Type[])null)
				{
					priority = 800
				}, new HarmonyMethod(typeof(Plugin), "ComputePost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)AccessTools.Method(typeof(WorldStateCompiler), "Compute", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "WsPost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)AccessTools.Method(typeof(HeightMapBuilder), "Compute", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "HmPost", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				L.LogInfo((object)"RiverheimCache patches applied");
			}
			catch (Exception ex)
			{
				L.LogError((object)("RiverheimCache disabled (patch failed): " + ex));
			}
		}

		private static void WsPost(WorldState __result)
		{
			capWs = __result;
		}

		private static void HmPost(HeightMap __result)
		{
			capHm = __result;
		}

		private static string Sha(byte[] b)
		{
			using SHA256 sHA = SHA256.Create();
			return Hex(sHA.ComputeHash(b));
		}

		private void SeedFromPluginFolder()
		{
			try
			{
				string directoryName = Path.GetDirectoryName(base.Info.Location);
				if (string.IsNullOrEmpty(directoryName) || !Directory.Exists(directoryName))
				{
					return;
				}
				string text = Path.Combine(Paths.CachePath, "RiverheimCache");
				Directory.CreateDirectory(text);
				int num = 0;
				foreach (string item in from x in Directory.GetFiles(directoryName)
					where x.EndsWith(".rhc", StringComparison.OrdinalIgnoreCase) || x.EndsWith(".rhc.verified", StringComparison.OrdinalIgnoreCase)
					select x)
				{
					string text2 = Path.Combine(text, Path.GetFileName(item));
					if (!File.Exists(text2) && !File.Exists(text2 + ".bad") && !File.Exists(Path.ChangeExtension(text2, null) + ".bad"))
					{
						try
						{
							File.Copy(item, text2, overwrite: false);
							num++;
						}
						catch (Exception ex)
						{
							L.LogWarning((object)("RiverheimCache seed copy failed (harmless): " + Path.GetFileName(item) + " " + ex.Message));
						}
					}
				}
				if (num > 0)
				{
					L.LogMessage((object)("RiverheimCache seeded " + num + " file(s) from plugin folder into " + text));
				}
			}
			catch (Exception ex2)
			{
				L.LogWarning((object)("RiverheimCache seeding failed (harmless): " + ex2.Message));
			}
		}

		private static string Hex(byte[] b)
		{
			return BitConverter.ToString(b).Replace("-", "");
		}

		private static string BuildKey(int seed, WorldGenerationConfig cfg)
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("fmt=").Append(1).Append("|seed=")
				.Append(seed);
			PluginInfo val = Chainloader.PluginInfos["dev.gurebu.riverheim.stable"];
			stringBuilder.Append("|rh=").Append(val.Metadata.Version);
			stringBuilder.Append("|rhdll=").Append(Sha(File.ReadAllBytes(typeof(GeneratorPipeline).Assembly.Location)));
			stringBuilder.Append("|game=").Append(Version.GetVersionString(false));
			stringBuilder.Append("|cfg=").Append(Sha(GraphSerializer.Serialize(cfg)));
			return stringBuilder.ToString();
		}

		private static bool ComputePre(GeneratorPipeline __instance, int seed, object masterConfig)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Expected O, but got Unknown
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Expected O, but got Unknown
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			capWs = null;
			capHm = null;
			curKey = null;
			curPath = null;
			curVerifyRun = false;
			curLoadedSha = null;
			curLoadedRenderer = null;
			if (!Enabled.Value)
			{
				return true;
			}
			try
			{
				if (!(masterConfig is WorldGenerationConfig cfg))
				{
					return true;
				}
				curSeed = seed;
				curKey = BuildKey(seed, cfg);
				string text;
				using (SHA256 sHA = SHA256.Create())
				{
					text = Hex(sHA.ComputeHash(Encoding.UTF8.GetBytes(curKey))).Substring(0, 16);
				}
				string text2 = Path.Combine(Paths.CachePath, "RiverheimCache");
				Directory.CreateDirectory(text2);
				string text3 = "world";
				try
				{
					text3 = San(ZNet.World?.m_name ?? "world");
				}
				catch
				{
				}
				curPath = Path.Combine(text2, text3 + "_" + seed + "_" + text + ".rhc");
				if (File.Exists(curPath + ".bad"))
				{
					L.LogWarning((object)"cache key previously failed verification; computing normally");
					curPath = null;
					return true;
				}
				if (!File.Exists(curPath))
				{
					L.LogMessage((object)("Riverheim cache MISS (" + curPath + "); computing normally, will save"));
					return true;
				}
				Stopwatch stopwatch = Stopwatch.StartNew();
				byte[] sha;
				object[] obj2 = (object[])GraphSerializer.Deserialize(ReadFile(curPath, curKey, out sha));
				WorldRenderer r = MakeRenderer(ws: (WorldState)obj2[0], hm: (HeightMap)obj2[1], seed: seed, cfg: cfg);
				long elapsedMilliseconds = stopwatch.ElapsedMilliseconds;
				if (VerifyEveryLoad.Value || (VerifyFirstLoad.Value && !File.Exists(curPath + ".verified")))
				{
					curVerifyRun = true;
					curLoadedSha = sha;
					curLoadedRenderer = r;
					L.LogMessage((object)$"Riverheim cache loaded in {elapsedMilliseconds}ms; VERIFY run: computing normally too and comparing");
					return true;
				}
				PutResult(__instance, r);
				L.LogMessage((object)$"Riverheim cache HIT: loaded world gen from cache in {elapsedMilliseconds}ms (skipped pipeline) {curPath}");
				curPath = null;
				return false;
			}
			catch (Exception ex)
			{
				L.LogWarning((object)("Riverheim cache load failed, computing normally: " + ex));
				try
				{
					if (curPath != null && File.Exists(curPath))
					{
						File.Delete(curPath);
					}
				}
				catch
				{
				}
				curVerifyRun = false;
				curLoadedRenderer = null;
				return true;
			}
		}

		private static void ComputePost(GeneratorPipeline __instance, int seed, object masterConfig)
		{
			//IL_001e: 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)
			try
			{
				if (curPath == null || !(masterConfig is WorldGenerationConfig))
				{
					return;
				}
				WorldGenerationConfig cfg = (WorldGenerationConfig)masterConfig;
				WorldState ws = capWs;
				HeightMap hm = capHm;
				if (ws == null || hm == (HeightMap)null)
				{
					L.LogWarning((object)"did not capture WorldState/HeightMap; not caching");
					return;
				}
				WorldRenderer real = __instance.GetResult<WorldRenderer>("Rendering/WorldRenderer");
				string path = curPath;
				string key = curKey;
				bool verifyRun = curVerifyRun;
				byte[] loadedSha = curLoadedSha;
				WorldRenderer loadedR = curLoadedRenderer;
				Thread thread = new Thread((ThreadStart)delegate
				{
					//IL_0193: Unknown result type (might be due to invalid IL or missing references)
					//IL_019b: Unknown result type (might be due to invalid IL or missing references)
					//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
					//IL_01ad: Expected O, but got Unknown
					//IL_01ad: Expected O, but got Unknown
					try
					{
						Stopwatch stopwatch = Stopwatch.StartNew();
						byte[] array = GraphSerializer.Serialize(new object[2] { ws, hm });
						byte[] array2;
						using (SHA256 sHA = SHA256.Create())
						{
							array2 = sHA.ComputeHash(array);
						}
						if (verifyRun)
						{
							bool flag = loadedSha != null && array2.SequenceEqual(loadedSha);
							string why;
							bool flag2 = flag && SamplesEqual(real, loadedR, SamplePoints.Value, out why);
							if (flag && flag2)
							{
								File.WriteAllText(path + ".verified", DateTime.Now.ToString("o"));
								L.LogMessage((object)$"Riverheim cache VERIFIED: computed data byte-identical to cache ({(double)array.Length / 1048576.0:0.0} MB) and {SamplePoints.Value} samples match; future joins use cache");
							}
							else
							{
								File.WriteAllText(path + ".bad", "mismatch bytes=" + flag + " samples=" + flag2);
								TryDel(path);
								L.LogError((object)("Riverheim cache verification FAILED (data " + (flag ? "same" : "differs") + "); cache disabled for this key"));
							}
						}
						else
						{
							object[] array3 = (object[])GraphSerializer.Deserialize(array);
							if (!GraphSerializer.Serialize(array3).SequenceEqual(array))
							{
								L.LogError((object)"round-trip mismatch; not caching");
							}
							else
							{
								WorldRenderer b = MakeRenderer(seed, cfg, (WorldState)array3[0], (HeightMap)array3[1]);
								if (!SamplesEqual(real, b, SamplePoints.Value, out var why2))
								{
									L.LogError((object)("cache-built renderer differs from computed (" + why2 + "); not caching"));
									File.WriteAllText(path + ".bad", why2);
								}
								else
								{
									WriteFile(path, key, array, array2);
									Prune(path);
									L.LogMessage((object)$"Riverheim cache SAVED {(double)array.Length / 1048576.0:0.0} MB in {stopwatch.ElapsedMilliseconds}ms (round-trip + {SamplePoints.Value} samples OK): {path}");
								}
							}
						}
					}
					catch (Exception ex2)
					{
						L.LogWarning((object)("Riverheim cache save failed (harmless): " + ex2));
					}
				});
				thread.IsBackground = true;
				thread.Priority = ThreadPriority.BelowNormal;
				thread.Name = "RiverheimCacheSave";
				thread.Start();
			}
			catch (Exception ex)
			{
				L.LogWarning((object)("Riverheim cache post failed (harmless): " + ex));
			}
			finally
			{
				capWs = null;
				capHm = null;
			}
		}

		private static WorldRenderer MakeRenderer(int seed, WorldGenerationConfig cfg, WorldState ws, HeightMap hm)
		{
			//IL_0001: 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_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_0029: Expected O, but got Unknown
			return new WorldRenderer(new RandomDirectory(seed).Sub("Rendering"), cfg.common, cfg.rendering.renderer, ws, hm);
		}

		private static void PutResult(GeneratorPipeline p, WorldRenderer r)
		{
			object value = AccessTools.Field(typeof(GeneratorPipeline), "cache").GetValue(p);
			AccessTools.Method(value.GetType(), "Put", (Type[])null, (Type[])null).Invoke(value, new object[2] { "Rendering/WorldRenderer", r });
		}

		private unsafe static bool SamplesEqual(WorldRenderer a, WorldRenderer b, int n, out string why)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			why = null;
			Random random = new Random(12345);
			double num = 10600.0;
			Rfloat2 val = default(Rfloat2);
			for (int i = 0; i < n; i++)
			{
				((Rfloat2)(ref val))..ctor((random.NextDouble() * 2.0 - 1.0) * num, (random.NextDouble() * 2.0 - 1.0) * num);
				PointData val2 = a.RenderPoint(val);
				PointData val3 = b.RenderPoint(val);
				if (val2.biome != val3.biome || val2.height != val3.height || val2.fertility != val3.fertility || val2.lava != val3.lava || val2.snow != val3.snow)
				{
					why = $"RenderPoint differs at {val}: {val2.height} vs {val3.height}";
					return false;
				}
				if (a.GetBiome(val) != b.GetBiome(val))
				{
					why = "GetBiome differs at " + ((object)(*(Rfloat2*)(&val))/*cast due to .constrained prefix*/).ToString();
					return false;
				}
				if (a.GetForestation(val) != b.GetForestation(val))
				{
					why = "GetForestation differs at " + ((object)(*(Rfloat2*)(&val))/*cast due to .constrained prefix*/).ToString();
					return false;
				}
			}
			return true;
		}

		private static void WriteFile(string path, string key, byte[] payload, byte[] sha)
		{
			string text = path + ".tmp";
			using (FileStream output = new FileStream(text, FileMode.Create, FileAccess.Write))
			{
				using BinaryWriter binaryWriter = new BinaryWriter(output);
				binaryWriter.Write("RHCACHE1");
				binaryWriter.Write(key);
				binaryWriter.Write(payload.Length);
				binaryWriter.Write(sha);
				binaryWriter.Write(payload);
			}
			TryDel(path);
			TryDel(path + ".verified");
			File.Move(text, path);
		}

		private static byte[] ReadFile(string path, string key, out byte[] sha)
		{
			using FileStream input = File.OpenRead(path);
			using BinaryReader binaryReader = new BinaryReader(input);
			if (binaryReader.ReadString() != "RHCACHE1")
			{
				throw new InvalidDataException("bad magic");
			}
			if (binaryReader.ReadString() != key)
			{
				throw new InvalidDataException("key mismatch");
			}
			int num = binaryReader.ReadInt32();
			sha = binaryReader.ReadBytes(32);
			byte[] array = binaryReader.ReadBytes(num);
			if (array.Length != num)
			{
				throw new InvalidDataException("truncated");
			}
			using (SHA256 sHA = SHA256.Create())
			{
				if (!sHA.ComputeHash(array).SequenceEqual(sha))
				{
					throw new InvalidDataException("checksum mismatch");
				}
			}
			return array;
		}

		private static void Prune(string keep)
		{
			try
			{
				string fileName = Path.GetFileName(keep);
				string text = fileName.Substring(0, fileName.LastIndexOf('_') + 1);
				string[] files = Directory.GetFiles(Path.GetDirectoryName(keep), text + "*");
				foreach (string text2 in files)
				{
					if (!text2.StartsWith(keep))
					{
						TryDel(text2);
					}
				}
			}
			catch
			{
			}
		}

		private static void TryDel(string f)
		{
			try
			{
				if (File.Exists(f))
				{
					File.Delete(f);
				}
			}
			catch
			{
			}
		}

		private static string San(string s)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				s = s.Replace(oldChar, '_');
			}
			return s;
		}
	}
	public static class GraphSerializer
	{
		private sealed class RefEq : IEqualityComparer<object>
		{
			public new bool Equals(object a, object b)
			{
				return a == b;
			}

			public int GetHashCode(object o)
			{
				return RuntimeHelpers.GetHashCode(o);
			}
		}

		private static readonly Dictionary<Type, FieldInfo[]> fieldCache = new Dictionary<Type, FieldInfo[]>();

		private static readonly HashSet<string> okBclGeneric = new HashSet<string>
		{
			"System.Collections.Generic.List`1", "System.Collections.Generic.Dictionary`2", "System.Collections.Generic.HashSet`1", "System.Nullable`1", "System.Collections.Generic.KeyValuePair`2", "System.ValueTuple`1", "System.ValueTuple`2", "System.ValueTuple`3", "System.ValueTuple`4", "System.ValueTuple`5",
			"System.ValueTuple`6", "System.ValueTuple`7"
		};

		private static FieldInfo[] Fields(Type t)
		{
			lock (fieldCache)
			{
				if (fieldCache.TryGetValue(t, out var value))
				{
					return value;
				}
				List<FieldInfo> list = new List<FieldInfo>();
				Type type = t;
				while (type != null && type != typeof(object) && type != typeof(ValueType))
				{
					list.InsertRange(0, from x in type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
						orderby x.MetadataToken
						select x);
					type = type.BaseType;
				}
				value = list.ToArray();
				fieldCache[t] = value;
				return value;
			}
		}

		private static bool IsDict(Type t)
		{
			if (t.IsGenericType)
			{
				return t.GetGenericTypeDefinition() == typeof(Dictionary<, >);
			}
			return false;
		}

		private static bool IsSet(Type t)
		{
			if (t.IsGenericType)
			{
				return t.GetGenericTypeDefinition() == typeof(HashSet<>);
			}
			return false;
		}

		private static void Check(Type t)
		{
			if (t.IsPointer || t.IsByRef || t == typeof(IntPtr) || t == typeof(UIntPtr) || typeof(Type).IsAssignableFrom(t) || typeof(MemberInfo).IsAssignableFrom(t))
			{
				throw new NotSupportedException("unsupported type " + t);
			}
			if (t.IsArray)
			{
				Check(t.GetElementType());
				return;
			}
			if (t.IsGenericType)
			{
				Type[] genericArguments = t.GetGenericArguments();
				for (int i = 0; i < genericArguments.Length; i++)
				{
					Check(genericArguments[i]);
				}
			}
			Assembly assembly = t.Assembly;
			string name = assembly.GetName().Name;
			if (!(assembly == typeof(object).Assembly))
			{
				switch (name)
				{
				case "System":
				case "System.Core":
				case "netstandard":
					break;
				default:
					if (typeof(Object).IsAssignableFrom(t))
					{
						throw new NotSupportedException("unity object " + t);
					}
					return;
				}
			}
			if (t.IsPrimitive || t.IsEnum || t == typeof(string) || t == typeof(object) || t == typeof(decimal) || (t.IsGenericType && okBclGeneric.Contains(t.GetGenericTypeDefinition().FullName)))
			{
				return;
			}
			throw new NotSupportedException("unsupported BCL type " + t);
		}

		public static byte[] Serialize(object root)
		{
			MemoryStream memoryStream = new MemoryStream();
			BinaryWriter w = new BinaryWriter(memoryStream);
			Dictionary<object, int> ids = new Dictionary<object, int>(new RefEq());
			Queue<object> queue = new Queue<object>();
			Dictionary<Type, int> types = new Dictionary<Type, int>();
			WRef(root);
			while (queue.Count > 0)
			{
				object obj = queue.Dequeue();
				Type type = obj.GetType();
				if (obj is Array array)
				{
					Type elementType = type.GetElementType();
					if (elementType.IsPrimitive)
					{
						byte[] array2 = new byte[Buffer.ByteLength(array)];
						Buffer.BlockCopy(array, 0, array2, 0, array2.Length);
						w.Write(array2);
						continue;
					}
					int[] array3 = new int[array.Rank];
					int length = array.Length;
					for (int i = 0; i < length; i++)
					{
						Unflat(array, i, array3);
						WVal(elementType, array.GetValue(array3));
					}
				}
				else if (IsDict(type))
				{
					Type[] genericArguments = type.GetGenericArguments();
					IDictionary dictionary = (IDictionary)obj;
					w.Write(dictionary.Count);
					foreach (DictionaryEntry item in dictionary)
					{
						WVal(genericArguments[0], item.Key);
						WVal(genericArguments[1], item.Value);
					}
				}
				else if (IsSet(type))
				{
					Type[] genericArguments2 = type.GetGenericArguments();
					List<object> list = ((IEnumerable)obj).Cast<object>().ToList();
					w.Write(list.Count);
					foreach (object item2 in list)
					{
						WVal(genericArguments2[0], item2);
					}
				}
				else
				{
					FieldInfo[] array4 = Fields(type);
					foreach (FieldInfo fieldInfo in array4)
					{
						WVal(fieldInfo.FieldType, fieldInfo.GetValue(obj));
					}
				}
			}
			w.Flush();
			return memoryStream.ToArray();
			void WRef(object o)
			{
				int value;
				if (o == null)
				{
					w.Write(-1);
				}
				else if (ids.TryGetValue(o, out value))
				{
					w.Write(value);
				}
				else
				{
					value = ids.Count;
					ids[o] = value;
					w.Write(value);
					Type type2 = o.GetType();
					if (type2.IsPrimitive || type2.IsEnum)
					{
						throw new NotSupportedException("boxed primitive " + type2);
					}
					WType(type2);
					if (o is string value2)
					{
						w.Write(value2);
					}
					else if (o is Delegate obj2)
					{
						if (obj2.GetInvocationList().Length != 1)
						{
							throw new NotSupportedException("multicast delegate");
						}
						MethodInfo method = obj2.Method;
						object target = obj2.Target;
						byte value3;
						if (target == null)
						{
							value3 = 0;
						}
						else
						{
							FieldInfo field = target.GetType().GetField("<>9", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
							if (!(field != null) || field.GetValue(null) != target)
							{
								throw new NotSupportedException("delegate with captured target " + method);
							}
							value3 = 1;
						}
						if (method.DeclaringType.Assembly == typeof(object).Assembly)
						{
							throw new NotSupportedException("BCL delegate " + method);
						}
						w.Write(value3);
						WType(method.DeclaringType);
						w.Write(method.MetadataToken);
					}
					else
					{
						if (o is Array array5)
						{
							w.Write(array5.Rank);
							for (int k = 0; k < array5.Rank; k++)
							{
								w.Write(array5.GetLength(k));
							}
						}
						if (IsDict(type2) || IsSet(type2))
						{
							object value4 = type2.GetProperty("Comparer").GetValue(o);
							object value5 = typeof(EqualityComparer<>).MakeGenericType(type2.GetGenericArguments()[0]).GetProperty("Default").GetValue(null);
							if (value4.GetType() != value5.GetType())
							{
								throw new NotSupportedException("custom comparer on " + type2);
							}
						}
						queue.Enqueue(o);
					}
				}
			}
			void WType(Type t)
			{
				if (types.TryGetValue(t, out var value))
				{
					w.Write(value);
				}
				else
				{
					Check(t);
					value = types.Count;
					types[t] = value;
					w.Write(value);
					if (t.IsArray)
					{
						w.Write((byte)2);
						WType(t.GetElementType());
						int arrayRank = t.GetArrayRank();
						bool flag = t == t.GetElementType().MakeArrayType();
						w.Write((!flag) ? arrayRank : 0);
					}
					else if (t.IsGenericType && !t.IsGenericTypeDefinition)
					{
						w.Write((byte)1);
						WType(t.GetGenericTypeDefinition());
						Type[] genericArguments3 = t.GetGenericArguments();
						w.Write(genericArguments3.Length);
						Type[] array5 = genericArguments3;
						for (int k = 0; k < array5.Length; k++)
						{
							WType(array5[k]);
						}
					}
					else
					{
						w.Write((byte)0);
						w.Write(t.Assembly.GetName().Name);
						w.Write(t.FullName);
					}
				}
			}
			void WVal(Type t, object v)
			{
				if (t.IsEnum)
				{
					t = Enum.GetUnderlyingType(t);
				}
				if (t.IsPrimitive)
				{
					WPrim(w, t, v);
				}
				else if (t == typeof(decimal))
				{
					w.Write((decimal)v);
				}
				else if (t.IsValueType)
				{
					Check(t);
					FieldInfo[] array5 = Fields(t);
					foreach (FieldInfo fieldInfo2 in array5)
					{
						WVal(fieldInfo2.FieldType, fieldInfo2.GetValue(v));
					}
				}
				else
				{
					WRef(v);
				}
			}
		}

		private static void Unflat(Array a, int k, int[] idx)
		{
			for (int num = a.Rank - 1; num >= 0; num--)
			{
				int length = a.GetLength(num);
				idx[num] = k % length;
				k /= length;
			}
		}

		private static void WPrim(BinaryWriter w, Type t, object v)
		{
			switch (Type.GetTypeCode(t))
			{
			case TypeCode.Boolean:
				w.Write((bool)v);
				break;
			case TypeCode.Char:
				w.Write((ushort)(char)v);
				break;
			case TypeCode.SByte:
				w.Write((sbyte)v);
				break;
			case TypeCode.Byte:
				w.Write((byte)v);
				break;
			case TypeCode.Int16:
				w.Write((short)v);
				break;
			case TypeCode.UInt16:
				w.Write((ushort)v);
				break;
			case TypeCode.Int32:
				w.Write((int)v);
				break;
			case TypeCode.UInt32:
				w.Write((uint)v);
				break;
			case TypeCode.Int64:
				w.Write((long)v);
				break;
			case TypeCode.UInt64:
				w.Write((ulong)v);
				break;
			case TypeCode.Single:
				w.Write((float)v);
				break;
			case TypeCode.Double:
				w.Write((double)v);
				break;
			default:
				throw new NotSupportedException("primitive " + t);
			}
		}

		private static object RPrim(BinaryReader r, Type t)
		{
			return Type.GetTypeCode(t) switch
			{
				TypeCode.Boolean => r.ReadBoolean(), 
				TypeCode.Char => (char)r.ReadUInt16(), 
				TypeCode.SByte => r.ReadSByte(), 
				TypeCode.Byte => r.ReadByte(), 
				TypeCode.Int16 => r.ReadInt16(), 
				TypeCode.UInt16 => r.ReadUInt16(), 
				TypeCode.Int32 => r.ReadInt32(), 
				TypeCode.UInt32 => r.ReadUInt32(), 
				TypeCode.Int64 => r.ReadInt64(), 
				TypeCode.UInt64 => r.ReadUInt64(), 
				TypeCode.Single => r.ReadSingle(), 
				TypeCode.Double => r.ReadDouble(), 
				_ => throw new NotSupportedException("primitive " + t), 
			};
		}

		public static object Deserialize(byte[] data)
		{
			BinaryReader r = new BinaryReader(new MemoryStream(data, writable: false));
			List<object> objs = new List<object>();
			Queue<object> queue = new Queue<object>();
			List<Type> types = new List<Type>();
			List<Action> list = new List<Action>();
			object result = RRef();
			while (queue.Count > 0)
			{
				object o = queue.Dequeue();
				Type type = o.GetType();
				if (o is Array array)
				{
					Type elementType = type.GetElementType();
					if (elementType.IsPrimitive)
					{
						int num = Buffer.ByteLength(array);
						byte[] array2 = r.ReadBytes(num);
						if (array2.Length != num)
						{
							throw new EndOfStreamException();
						}
						Buffer.BlockCopy(array2, 0, array, 0, num);
					}
					else
					{
						int[] array3 = new int[array.Rank];
						int length = array.Length;
						for (int i = 0; i < length; i++)
						{
							Unflat(array, i, array3);
							array.SetValue(RVal(elementType), array3);
						}
					}
				}
				else if (IsDict(type))
				{
					Type[] genericArguments = type.GetGenericArguments();
					int num2 = r.ReadInt32();
					IDictionary d = (IDictionary)o;
					List<(object, object)> kv = new List<(object, object)>(num2);
					for (int j = 0; j < num2; j++)
					{
						object item = RVal(genericArguments[0]);
						object item2 = RVal(genericArguments[1]);
						kv.Add((item, item2));
					}
					list.Add(delegate
					{
						foreach (var (key, value) in kv)
						{
							d.Add(key, value);
						}
					});
				}
				else if (IsSet(type))
				{
					Type[] genericArguments2 = type.GetGenericArguments();
					int num3 = r.ReadInt32();
					List<object> items = new List<object>(num3);
					for (int num4 = 0; num4 < num3; num4++)
					{
						items.Add(RVal(genericArguments2[0]));
					}
					MethodInfo add = type.GetMethod("Add");
					list.Add(delegate
					{
						foreach (object item3 in items)
						{
							add.Invoke(o, new object[1] { item3 });
						}
					});
				}
				else
				{
					FieldInfo[] array4 = Fields(type);
					foreach (FieldInfo fieldInfo in array4)
					{
						fieldInfo.SetValue(o, RVal(fieldInfo.FieldType));
					}
				}
			}
			foreach (Action item4 in list)
			{
				item4();
			}
			if (r.BaseStream.Position != r.BaseStream.Length)
			{
				throw new InvalidDataException("trailing data");
			}
			return result;
			object RRef()
			{
				int num6 = r.ReadInt32();
				if (num6 == -1)
				{
					return null;
				}
				if (num6 < objs.Count)
				{
					return objs[num6];
				}
				if (num6 != objs.Count)
				{
					throw new InvalidDataException("object id order");
				}
				Type type2 = RType();
				object obj;
				if (type2 == typeof(string))
				{
					obj = r.ReadString();
					objs.Add(obj);
					return obj;
				}
				if (typeof(Delegate).IsAssignableFrom(type2))
				{
					byte num7 = r.ReadByte();
					Type type3 = RType();
					int metadataToken = r.ReadInt32();
					MethodInfo methodInfo = (MethodInfo)type3.Module.ResolveMethod(metadataToken, type3.IsGenericType ? type3.GetGenericArguments() : null, null);
					if (type3.IsGenericType)
					{
						methodInfo = (MethodInfo)MethodBase.GetMethodFromHandle(methodInfo.MethodHandle, type3.TypeHandle);
					}
					object obj2 = ((num7 == 0) ? null : type3.GetField("<>9", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null));
					obj = ((obj2 == null) ? Delegate.CreateDelegate(type2, methodInfo) : Delegate.CreateDelegate(type2, obj2, methodInfo));
					objs.Add(obj);
					return obj;
				}
				if (!type2.IsArray)
				{
					obj = ((!IsDict(type2) && !IsSet(type2)) ? FormatterServices.GetUninitializedObject(type2) : Activator.CreateInstance(type2));
				}
				else
				{
					int num8 = r.ReadInt32();
					int[] array5 = new int[num8];
					for (int k = 0; k < num8; k++)
					{
						array5[k] = r.ReadInt32();
					}
					obj = Array.CreateInstance(type2.GetElementType(), array5);
				}
				objs.Add(obj);
				queue.Enqueue(obj);
				return obj;
			}
			Type RType()
			{
				int num6 = r.ReadInt32();
				if (num6 < types.Count)
				{
					return types[num6];
				}
				if (num6 != types.Count)
				{
					throw new InvalidDataException("type id order");
				}
				types.Add(null);
				Type type2;
				switch (r.ReadByte())
				{
				case 2:
				{
					Type type4 = RType();
					int num9 = r.ReadInt32();
					type2 = ((num9 == 0) ? type4.MakeArrayType() : type4.MakeArrayType(num9));
					break;
				}
				case 1:
				{
					Type type3 = RType();
					int num7 = r.ReadInt32();
					Type[] array5 = new Type[num7];
					for (int num8 = 0; num8 < num7; num8++)
					{
						array5[num8] = RType();
					}
					type2 = type3.MakeGenericType(array5);
					break;
				}
				default:
				{
					string an = r.ReadString();
					string name = r.ReadString();
					type2 = (AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly x) => x.GetName().Name == an) ?? throw new InvalidDataException("assembly " + an)).GetType(name, throwOnError: true);
					break;
				}
				}
				Check(type2);
				types[num6] = type2;
				return type2;
			}
			object RVal(Type t)
			{
				if (t.IsEnum)
				{
					return Enum.ToObject(t, RPrim(r, Enum.GetUnderlyingType(t)));
				}
				if (t.IsPrimitive)
				{
					return RPrim(r, t);
				}
				if (t == typeof(decimal))
				{
					return r.ReadDecimal();
				}
				if (t.IsValueType)
				{
					object uninitializedObject = FormatterServices.GetUninitializedObject(t);
					FieldInfo[] array5 = Fields(t);
					foreach (FieldInfo fieldInfo2 in array5)
					{
						fieldInfo2.SetValue(uninitializedObject, RVal(fieldInfo2.FieldType));
					}
					return uninitializedObject;
				}
				return RRef();
			}
		}
	}
}
namespace DropNSpawnCache
{
	public class Plugin : ModuleBehaviour
	{
		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> cSig;

		internal static ConfigEntry<bool> cPick;

		internal static ConfigEntry<bool> cNull;

		internal static ConfigEntry<bool> cStats;

		internal static ConfigEntry<float> cRevalidate;

		internal static ConfigEntry<float> cSlice;

		internal static ConfigEntry<float> cDeferSec;

		internal static ConfigEntry<bool> cPace;

		internal static ConfigEntry<bool> cDefer;

		private Harmony h;

		private float nextStats;

		private void Awake()
		{
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Expected O, but got Unknown
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			Log = base.Logger;
			cSig = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Features", "CacheSignatures", true, "Cache DropNSpawn object-domain prefab signatures (invalidated when ZNetScene/ObjectDB prefab lists change, on DropNSpawn game-data refresh, and every RevalidateSeconds).");
			cRevalidate = base.Config.Bind<float>("Optional - DropNSpawn Cache - Features", "RevalidateSeconds", 30f, "Force a real signature recompute at most this often (safety net). 0 = never.");
			cPick = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Features", "SkipRedundantPickableReconcile", true, "Skip queuing live reconcile for freshly spawned Pickables whose prefab only has unconditional Pickable overrides and whose values already match the configured prefab.");
			cNull = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Features", "GuardCloneDropTable", true, "Return null / empty copy instead of throwing when DropNSpawn snapshots a null DropTable.");
			cStats = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Diagnostics", "LogStats", true, "Log a one-line counter summary every 60 s while counters change.");
			cPace = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Pacing", "PaceRuntimeWork", true, "1.0.1: DropNSpawn's per-frame runtime work (payload sync, snapshot build, reconcile) has a 1 ms target but a single step can take much longer. After a frame where it took longer than RefreshSliceMs, skip it for ceil(took/RefreshSliceMs) frames so slow steps do not stack into a stutter. Never paces while loading or with no local player.");
			cSlice = base.Config.Bind<float>("Optional - DropNSpawn Cache - Pacing", "RefreshSliceMs", 8f, new ConfigDescription("Per-frame cost above which the following frames are skipped.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), Array.Empty<object>()));
			cDefer = base.Config.Bind<bool>("Optional - DropNSpawn Cache - Pacing", "DeferUntilAfterSpawn", false, "1.0.1: skip DropNSpawn runtime work entirely until DeferSec after the local player spawns (and after each teleport). Off by default: work queued during load then runs a few seconds later.");
			cDeferSec = base.Config.Bind<float>("Optional - DropNSpawn Cache - Pacing", "DeferSec", 3f, new ConfigDescription("Seconds after spawn/teleport for DeferUntilAfterSpawn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			h = new Harmony("vpf.ValheimPerformanceFixes.DropNSpawnCache");
			try
			{
				DNS.Init(h);
			}
			catch (Exception ex)
			{
				Log.LogError((object)("init failed, plugin inactive: " + ex));
			}
		}

		private void Update()
		{
			if (cStats.Value && !(Time.realtimeSinceStartup < nextStats))
			{
				nextStats = Time.realtimeSinceStartup + 60f;
				DNS.LogStats();
			}
		}
	}
	internal static class DNS
	{
		private struct Slot
		{
			public bool valid;

			public long key;

			public int value;

			public float at;
		}

		private static Type odm;

		private static Type plugin;

		private static FieldInfo fPending;

		private static FieldInfo fLastApplied;

		private static FieldInfo fSyncReady;

		private static FieldInfo fProfile;

		private static FieldInfo fRuntime;

		private static MethodInfo mTryKinds;

		private static MethodInfo mHasCond;

		private static MethodInfo mDeferred;

		private static MethodInfo mTrack;

		private static PropertyInfo pActive;

		private static PropertyInfo pCfgSig;

		private static Func<GameObject, string> getPrefabName;

		private static object reloadObject;

		private static long sigHits;

		private static long sigMiss;

		private static long pickSkip;

		private static long pickQueue;

		private static long nullGuards;

		private static double sigMissMs;

		private static long lastTotal = -1L;

		private static Slot sGame;

		private static Slot sSnap;

		private static readonly Stopwatch sw = new Stopwatch();

		private static long pendingKey;

		private static bool computing;

		private static string condCacheSig;

		private static readonly Dictionary<string, bool> condFree = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private static readonly object[] kindArgs = new object[2];

		[ThreadStatic]
		private static string capturing;

		private static readonly HashSet<string> warned = new HashSet<string>();

		private static int skipFrames;

		private static long paceSkipped;

		private static long slowSteps;

		private static long deferredFrames;

		private static double slowMs;

		private static double maxMs;

		private static readonly Stopwatch psw = new Stopwatch();

		private static float readySince = -1f;

		public static void Init(Harmony h)
		{
			//IL_0770: Unknown result type (might be due to invalid IL or missing references)
			//IL_077e: Expected O, but got Unknown
			//IL_0799: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b8: Expected O, but got Unknown
			//IL_07b8: Expected O, but got Unknown
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Expected O, but got Unknown
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Expected O, but got Unknown
			//IL_0222: Expected O, but got Unknown
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Expected O, but got Unknown
			//IL_0330: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Expected O, but got Unknown
			//IL_0692: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a0: Expected O, but got Unknown
			Assembly assembly = null;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly2 in assemblies)
			{
				if (assembly2.GetName().Name == "DropNSpawn")
				{
					assembly = assembly2;
					break;
				}
			}
			if (assembly == null)
			{
				Plugin.Log.LogWarning((object)"DropNSpawn assembly not loaded; nothing to do.");
				return;
			}
			try
			{
				InitPacing(h, assembly);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("pacing init failed: " + ex.Message));
			}
			odm = assembly.GetType("DropNSpawn.ObjectDropManager");
			plugin = assembly.GetType("DropNSpawn.DropNSpawnPlugin");
			if (odm == null)
			{
				Plugin.Log.LogWarning((object)"DropNSpawn.ObjectDropManager not found; nothing to do.");
				return;
			}
			Type type = plugin;
			object obj;
			if ((object)type == null)
			{
				obj = null;
			}
			else
			{
				BepInPlugin? customAttribute = ((MemberInfo)type).GetCustomAttribute<BepInPlugin>();
				obj = ((customAttribute == null) ? null : customAttribute.Version?.ToString());
			}
			if (obj == null)
			{
				obj = "?";
			}
			string text = (string)obj;
			Plugin.Log.LogInfo((object)("DropNSpawn " + text + " detected" + ((text != "1.4.8") ? " (written for 1.4.8 - patches are guarded, check log)" : "")));
			Type typeFromHandle = typeof(DNS);
			if (Plugin.cSig.Value)
			{
				int num = 0;
				string[] array = new string[2] { "ComputeGameDataSignatureCore", "ComputeSnapshotSignatureCore" };
				foreach (string text2 in array)
				{
					MethodInfo methodInfo = AccessTools.Method(odm, text2, Type.EmptyTypes, (Type[])null);
					if (methodInfo == null || methodInfo.ReturnType != typeof(int))
					{
						Plugin.Log.LogWarning((object)("signature cache: " + text2 + " not found, skipped"));
						continue;
					}
					h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeFromHandle.GetMethod((text2 == "ComputeGameDataSignatureCore") ? "PreGame" : "PreSnap", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
					{
						priority = 800
					}, new HarmonyMethod(typeFromHandle.GetMethod((text2 == "ComputeGameDataSignatureCore") ? "PostGame" : "PostSnap", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
				}
				Type type2 = assembly.GetType("DropNSpawn.PluginRuntimeWorkCoordinator");
				MethodInfo methodInfo2 = ((type2 == null) ? null : AccessTools.Method(type2, "QueueGameDataRefresh", (Type[])null, (Type[])null));
				if (methodInfo2 != null)
				{
					h.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeFromHandle.GetMethod("Invalidate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				Type[] array2 = new Type[2]
				{
					typeof(ZNetScene),
					typeof(ObjectDB)
				};
				for (int i = 0; i < array2.Length; i++)
				{
					MethodInfo methodInfo3 = AccessTools.Method(array2[i], "Awake", (Type[])null, (Type[])null);
					if (methodInfo3 != null)
					{
						h.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeFromHandle.GetMethod("Invalidate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
				MethodInfo methodInfo4 = AccessTools.Method(typeof(ObjectDB), "CopyOtherDB", (Type[])null, (Type[])null);
				if (methodInfo4 != null)
				{
					h.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeFromHandle.GetMethod("Invalidate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				Plugin.Log.LogInfo((object)("signature cache: " + num + "/2 patched"));
			}
			if (Plugin.cPick.Value)
			{
				try
				{
					fPending = AccessTools.Field(odm, "_pendingSnapshotBuild");
					fLastApplied = AccessTools.Field(odm, "_lastAppliedGameDataSignature");
					fSyncReady = AccessTools.Field(odm, "_synchronizedPayloadReady");
					fProfile = AccessTools.Field(odm, "PrefabProfileCatalogState");
					fRuntime = AccessTools.Field(odm, "RuntimeState");
					mTryKinds = ((fProfile == null) ? null : AccessTools.Method(fProfile.FieldType, "TryGetReconcileKinds", (Type[])null, (Type[])null));
					pActive = ((fRuntime == null) ? null : AccessTools.Property(fRuntime.FieldType, "ActiveEntriesByPrefab"));
					pCfgSig = ((fRuntime == null) ? null : AccessTools.Property(fRuntime.FieldType, "ConfigurationSignature"));
					Type type3 = assembly.GetType("DropNSpawn.DropConditionEvaluator");
					mHasCond = ((type3 == null) ? null : AccessTools.Method(type3, "HasConditions", (Type[])null, (Type[])null));
					mDeferred = ((plugin == null) ? null : AccessTools.Method(plugin, "IsGameDataRefreshDeferred", (Type[])null, (Type[])null));
					Type type4 = plugin?.GetNestedType("ReloadDomain", BindingFlags.Public | BindingFlags.NonPublic);
					reloadObject = ((type4 == null) ? null : Enum.ToObject(type4, 1));
					mTrack = AccessTools.Method(odm, "TrackObjectInstance", new Type[1] { typeof(GameObject) }, (Type[])null);
					MethodInfo methodInfo5 = AccessTools.Method(odm, "GetPrefabName", new Type[1] { typeof(GameObject) }, (Type[])null);
					if (methodInfo5 != null && methodInfo5.ReturnType == typeof(string))
					{
						getPrefabName = (Func<GameObject, string>)Delegate.CreateDelegate(typeof(Func<GameObject, string>), methodInfo5);
					}
					Type nestedType = odm.GetNestedType("LiveObjectComponentKind", BindingFlags.Public | BindingFlags.NonPublic);
					MethodInfo methodInfo6 = ((nestedType == null) ? null : AccessTools.Method(odm, "QueueObjectInstanceReconcile", new Type[2]
					{
						typeof(GameObject),
						nestedType
					}, (Type[])null));
					if (methodInfo6 != null && fPending != null && fLastApplied != null && fLastApplied.FieldType == typeof(int?) && fSyncReady != null && mTryKinds != null && pActive != null && pCfgSig != null && mHasCond != null && mDeferred != null && reloadObject != null && mTrack != null && getPrefabName != null && nestedType != null && Convert.ToInt32(Enum.Parse(nestedType, "Pickable")) == 64)
					{
						h.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeFromHandle.GetMethod("PreQueue", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"pickable reconcile skip: patched");
					}
					else
					{
						Plugin.Log.LogWarning((object)"pickable reconcile skip: a required member is missing, skipped");
					}
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("pickable reconcile skip: setup failed, skipped: " + ex2.Message));
				}
			}
			if (!Plugin.cNull.Value)
			{
				return;
			}
			MethodInfo methodInfo7 = AccessTools.Method(odm, "CloneDropTable", new Type[1] { typeof(DropTable) }, (Type[])null);
			MethodInfo methodInfo8 = AccessTools.Method(odm, "CaptureSnapshot", new Type[1] { typeof(GameObject) }, (Type[])null);
			if (methodInfo7 != null && methodInfo7.ReturnType == typeof(DropTable))
			{
				h.Patch((MethodBase)methodInfo7, new HarmonyMethod(typeFromHandle.GetMethod("PreClone", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				if (methodInfo8 != null)
				{
					h.Patch((MethodBase)methodInfo8, new HarmonyMethod(typeFromHandle.GetMethod("PreCapture", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), new HarmonyMethod(typeFromHandle.GetMethod("PostCapture", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				Plugin.Log.LogInfo((object)"CloneDropTable null guard: patched");
			}
			else
			{
				Plugin.Log.LogWarning((object)"CloneDropTable null guard: target missing, skipped");
			}
		}

		private static void Invalidate()
		{
			sGame.valid = false;
			sSnap.valid = false;
		}

		private static long Fingerprint()
		{
			ZNetScene instance = ZNetScene.instance;
			ObjectDB instance2 = ObjectDB.instance;
			if (instance == null || instance2 == null || !Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance2))
			{
				return 0L;
			}
			long num = 1469598103934665603L;
			num = (num ^ ((Object)instance).GetInstanceID()) * 1099511628211L;
			num = (num ^ ((Object)instance2).GetInstanceID()) * 1099511628211L;
			num = Mix(num, instance.m_prefabs);
			num = Mix(num, instance.m_nonNetViewPrefabs);
			num = Mix(num, instance2.m_items);
			if (num != 0L)
			{
				return num;
			}
			return 1L;
		}

		private static long Mix(long hsh, List<GameObject> l)
		{
			if (l == null)
			{
				return hsh * 31;
			}
			hsh = (hsh ^ l.Count) * 1099511628211L;
			for (int i = 0; i < l.Count; i++)
			{
				GameObject val = l[i];
				hsh = (hsh ^ ((val != null) ? ((Object)val).GetInstanceID() : 0)) * 1099511628211L;
			}
			return hsh;
		}

		private static bool Pre(ref Slot s, ref int __result)
		{
			long num = Fingerprint();
			if (num == 0L)
			{
				s.valid = false;
				computing = false;
				return true;
			}
			float value = Plugin.cRevalidate.Value;
			if (s.valid && s.key == num && (value <= 0f || Time.realtimeSinceStartup - s.at < value))
			{
				__result = s.value;
				sigHits++;
				return false;
			}
			pendingKey = num;
			computing = true;
			sw.Restart();
			return true;
		}

		private static void Post(ref Slot s, int __result)
		{
			if (computing)
			{
				computing = false;
				sw.Stop();
				sigMiss++;
				sigMissMs += sw.Elapsed.TotalMilliseconds;
				if (__result == 0)
				{
					s.valid = false;
					return;
				}
				s.valid = true;
				s.key = pendingKey;
				s.value = __result;
				s.at = Time.realtimeSinceStartup;
			}
		}

		private static bool PreGame(ref int __result)
		{
			return Pre(ref sGame, ref __result);
		}

		private static void PostGame(int __result)
		{
			Post(ref sGame, __result);
		}

		private static bool PreSnap(ref int __result)
		{
			return Pre(ref sSnap, ref __result);
		}

		private static void PostSnap(int __result)
		{
			Post(ref sSnap, __result);
		}

		private static bool PreQueue(object[] __args)
		{
			try
			{
				if (Convert.ToInt32(__args[1]) != 64)
				{
					return true;
				}
				object obj = __args[0];
				GameObject val = (GameObject)((obj is GameObject) ? obj : null);
				if (!Object.op_Implicit((Object)(object)val))
				{
					return true;
				}
				if (fPending.GetValue(null) != null)
				{
					return true;
				}
				if (!((int?)fLastApplied.GetValue(null)).HasValue)
				{
					return true;
				}
				if (!(bool)fSyncReady.GetValue(null))
				{
					return true;
				}
				if ((bool)mDeferred.Invoke(null, new object[1] { reloadObject }))
				{
					return true;
				}
				string text = getPrefabName(val);
				if (string.IsNullOrEmpty(text))
				{
					return true;
				}
				object value = fProfile.GetValue(null);
				kindArgs[0] = text;
				kindArgs[1] = null;
				if (!(bool)mTryKinds.Invoke(value, kindArgs) || Convert.ToInt32(kindArgs[1]) != 64)
				{
					return true;
				}
				if (!ConditionFree(text))
				{
					return true;
				}
				ZNetScene instance = ZNetScene.instance;
				if (!Object.op_Implicit((Object)(object)instance))
				{
					return true;
				}
				GameObject prefab = instance.GetPrefab(text);
				if (!Object.op_Implicit((Object)(object)prefab) || (Object)(object)prefab == (Object)(object)val)
				{
					return true;
				}
				Pickable component = val.GetComponent<Pickable>();
				Pickable component2 = prefab.GetComponent<Pickable>();
				if (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component2) || !Same(component, component2))
				{
					return true;
				}
				mTrack.Invoke(null, new object[1] { val });
				pickSkip++;
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("pickable skip error, falling back: " + ex.Message));
				return true;
			}
		}

		private static bool ConditionFree(string prefab)
		{
			object value = fRuntime.GetValue(null);
			string a = (pCfgSig.GetValue(value) as string) ?? "";
			if (!string.Equals(a, condCacheSig, StringComparison.Ordinal))
			{
				condFree.Clear();
				condCacheSig = a;
			}
			if (condFree.TryGetValue(prefab, out var value2))
			{
				return value2;
			}
			value2 = false;
			if (pActive.GetValue(value) is IDictionary dictionary && dictionary.Contains(prefab) && dictionary[prefab] is IList { Count: >0 } list)
			{
				value2 = true;
				foreach (object item in list)
				{
					PropertyInfo propertyInfo = item?.GetType().GetProperty("Conditions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (item == null || propertyInfo == null || (bool)mHasCond.Invoke(null, new object[1] { propertyInfo.GetValue(item) }))
					{
						value2 = false;
						break;
					}
				}
			}
			condFree[prefab] = value2;
			return value2;
		}

		private static bool Same(Pickable a, Pickable b)
		{
			if ((Object)(object)a.m_itemPrefab != (Object)(object)b.m_itemPrefab || a.m_amount != b.m_amount || a.m_minAmountScaled != b.m_minAmountScaled || a.m_dontScale != b.m_dontScale || !string.Equals(a.m_overrideName, b.m_overrideName, StringComparison.Ordinal))
			{
				return false;
			}
			return SameTable(a.m_extraDrops, b.m_extraDrops);
		}

		private static bool SameTable(DropTable x, DropTable y)
		{
			//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_007c: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			if (x == null || y == null)
			{
				return x == y;
			}
			if (x.m_dropMin != y.m_dropMin || x.m_dropMax != y.m_dropMax || x.m_dropChance != y.m_dropChance || x.m_oneOfEach != y.m_oneOfEach)
			{
				return false;
			}
			List<DropData> drops = x.m_drops;
			List<DropData> drops2 = y.m_drops;
			if (drops == null || drops2 == null)
			{
				return drops == drops2;
			}
			if (drops.Count != drops2.Count)
			{
				return false;
			}
			for (int i = 0; i < drops.Count; i++)
			{
				DropData val = drops[i];
				DropData val2 = drops2[i];
				if ((Object)(object)val.m_item != (Object)(object)val2.m_item || val.m_stackMin != val2.m_stackMin || val.m_stackMax != val2.m_stackMax || val.m_weight != val2.m_weight || val.m_dontScale != val2.m_dontScale)
				{
					return false;
				}
			}
			return true;
		}

		private static void PreCapture(GameObject prefab)
		{
			capturing = (Object.op_Implicit((Object)(object)prefab) ? ((Object)prefab).name : "?");
		}

		private static void PostCapture()
		{
			capturing = null;
		}

		private static bool PreClone(DropTable source, ref DropTable __result)
		{
			//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: 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_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			if (source != null && source.m_drops != null)
			{
				return true;
			}
			nullGuards++;
			string text = capturing ?? "(runtime)";
			if (warned.Add(text))
			{
				Plugin.Log.LogInfo((object)("CloneDropTable guard: " + ((source == null) ? "null DropTable" : "DropTable with null m_drops") + " on " + text + " (DropNSpawn would have thrown NRE)"));
			}
			if (source == null)
			{
				__result = null;
				return false;
			}
			__result = new DropTable
			{
				m_dropMin = source.m_dropMin,
				m_dropMax = source.m_dropMax,
				m_dropChance = source.m_dropChance,
				m_oneOfEach = source.m_oneOfEach,
				m_drops = new List<DropData>()
			};
			return false;
		}

		public static void InitPacing(Harmony h, Assembly asm)
		{
			//IL_0057: 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_007f: Expected O, but got Unknown
			//IL_007f: Expected O, but got Unknown
			Type type = asm.GetType("DropNSpawn.PluginRuntimeWorkCoordinator");
			MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "ProcessUpdateFrame", (Type[])null, (Type[])null) : null);
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)"pacing: PluginRuntimeWorkCoordinator.ProcessUpdateFrame not found; pacing inactive");
				return;
			}
			h.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(DNS).GetMethod("PacePre", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(DNS).GetMethod("PaceFin", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null);
			Plugin.Log.LogInfo((object)"pacing: patched ProcessUpdateFrame");
		}

		private static bool Ready()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting())
			{
				return false;
			}
			Hud instance = Hud.instance;
			if ((Object)(object)instance != (Object)null)
			{
				try
				{
					Traverse val = Traverse.Create((object)instance).Field("m_loadingScreen");
					object value = val.GetValue();
					Component val2 = (Component)((value is Component) ? value : null);
					if ((Object)(object)val2 != (Object)null && val2.gameObject.activeInHierarchy && val.Property("alpha", (object[])null).GetValue<float>() > 0.01f)
					{
						return false;
					}
				}
				catch
				{
				}
			}
			return true;
		}

		private static bool PacePre()
		{
			bool flag = Ready();
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (!flag)
			{
				readySince = -1f;
				skipFrames = 0;
			}
			else if (readySince < 0f)
			{
				readySince = realtimeSinceStartup;
			}
			if (Plugin.cDefer.Value && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && (!flag || realtimeSinceStartup - readySince < Plugin.cDeferSec.Value))
			{
				deferredFrames++;
				return false;
			}
			if (Plugin.cPace.Value && flag && skipFrames > 0)
			{
				skipFrames--;
				paceSkipped++;
				return false;
			}
			psw.Restart();
			return true;
		}

		private static Exception PaceFin(Exception __exception)
		{
			if (psw.IsRunning)
			{
				psw.Stop();
				double totalMilliseconds = psw.Elapsed.TotalMilliseconds;
				if (totalMilliseconds > maxMs)
				{
					maxMs = totalMilliseconds;
				}
				if (Plugin.cPace.Value && totalMilliseconds > (double)Plugin.cSlice.Value)
				{
					slowSteps++;
					slowMs += totalMilliseconds;
					if (Ready())
					{
						skipFrames = (int)Math.Ceiling(totalMilliseconds / (double)Plugin.cSlice.Value);
					}
				}
			}
			return __exception;
		}

		public static void LogStats()
		{
			if (slowSteps + paceSkipped + deferredFrames > 0)
			{
				Plugin.Log.LogInfo((object)("pacing: slow steps=" + slowSteps + " (avg " + ((slowSteps > 0) ? (slowMs / (double)slowSteps).ToString("F1") : "-") + " ms, max " + maxMs.ToString("F1") + " ms) frames skipped=" + paceSkipped + " deferred=" + deferredFrames));
			}
			long num = sigHits + sigMiss + pickSkip + nullGuards;
			if (num != lastTotal)
			{
				lastTotal = num;
				Plugin.Log.LogInfo((object)("stats: signature cache hits=" + sigHits + " misses=" + sigMiss + " (avg miss " + ((sigMiss > 0) ? (sigMissMs / (double)sigMiss).ToString("F2") : "-") + " ms) | pickable reconciles skipped=" + pickSkip + " | CloneDropTable guards=" + nullGuards));
			}
		}
	}
}
namespace SpawnBudget
{
	public class Plugin : ModuleBehaviour
	{
		private class PStat
		{
			public int hash;

			public string name;

			public int n;

			public int nLoad;

			public double total;

			public double totalLoad;

			public double max;
		}

		public const string Ver = "1.7.5";

		private static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> SkipDistant;

		private static ConfigEntry<bool> LogSlow;

		private static ConfigEntry<bool> HmEnabled;

		private static ConfigEntry<float> BudgetMs;

		private static ConfigEntry<float> SlowMs;

		private static ConfigEntry<float> HmBudgetMs;

		private static ConfigEntry<float> FrameBudgetMs;

		private static ConfigEntry<float> HmProtect;

		private static ConfigEntry<float> ReportSec;

		private static ConfigEntry<int> HmMaxDefer;

		private static ConfigEntry<int> MaxPerFrame;

		private static ConfigEntry<bool> HmAllPaths;

		private static ConfigEntry<float> TailSec;

		private static ConfigEntry<float> ListObjMs;

		private static readonly Stopwatch frameSw = new Stopwatch();

		private static int frame = -1;

		private static double spawnMs;

		private static double hmMs;

		private static int hmRuns;

		private static Func<ZNetScene, ZDO, GameObject> createObj;

		private static bool objOk = true;

		private static bool engagedLogged;

		private static ConfigEntry<float> PrioRadius;

		private static ConfigEntry<float> CmbBudget;

		private static ConfigEntry<float> CmbDetect;

		private static ConfigEntry<float> CmbExit;

		private static ConfigEntry<float> CmbEase;

		private static ConfigEntry<int> PrioMax;

		private static ConfigEntry<bool> CmbOn;

		private static ConfigEntry<bool> AdOn;

		private static ConfigEntry<float> AdLen;

		private static ConfigEntry<float> AdAgg;

		private static ConfigEntry<float> AdLow;

		private static ConfigEntry<float> AdHigh;

		private static ConfigEntry<float> AdStr;

		private static ConfigEntry<float> AdRise;

		private static ConfigEntry<float> AdFall;

		private static ConfigEntry<float> AdFrameMs;

		private static int lastPendNear;

		private static int lastPendDist;

		private static float dtEma = 0.016f;

		private static float nextAdLog;

		private static ConfigEntry<bool> ArOn;

		private static ConfigEntry<float> ArBudget;

		private static ConfigEntry<float> ArRamp;

		private static ConfigEntry<float> PendStale;

		private static ConfigEntry<float> LocOverMs;

		private static ConfigEntry<float> CmbEnterHold;

		private static ConfigEntry<float> CmbExit2;

		private static ConfigEntry<int> ArDistMax;

		private static ConfigEntry<int> DistMax;

		private static ConfigEntry<int> LocMaxPF;

		private static float cmbAiSince = -1f;

		private static float cmbEnterAt;

		private static float arrivalEndAt = -999f;

		private static string cmbWhy = "";

		private static int sArrFrames;

		private static int sArrObjs;

		private static int sArrHit;

		private static int sArrEntered;

		private static int sLocDupTracked;

		private static int sLocSamePos;

		private static int sLocCapDelayed;

		private static int sLocSlowFrameDelayed;

		private static int lastPendDistStale;

		private static double sArrMax;

		private static bool arrivalWas;

		private static readonly Dictionary<ZDO, float> distSeen = new Dictionary<ZDO, float>();

		private static readonly Dictionary<ZDO, float> distSeen2 = new Dictionary<ZDO, float>();

		private static float nextPendCount;

		private static readonly List<ZDO> tmpD = new List<ZDO>();

		private static float cmbF;

		private static float cmbLastSig = -999f;

		private static float cmbNext;

		private static float cmbLastHit = -999f;

		private static float cmbLastT;

		private static bool cmbIn;

		private static int sCombatFrames;

		private static int sPrio;

		private static readonly List<ZDO> tmp = new List<ZDO>();

		private static readonly List<ZDO> empty = new List<ZDO>();

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

		private static int sBudgetHitFrames;

		private static int sNearDeferred;

		private static int sDistantSkips;

		private static int sHmDeferrals;

		private static int sHmForced;

		private static int sHmRuns;

		private static int sObjs;

		private static int sActiveFrames;

		private static int sOverOne;

		private static double sHmMax;

		private static double sHmTotal;

		private static double sMaxSpawn;

		private static double sSpawnTotal;

		private static float nextReport;

		private static StreamWriter log;

		private static readonly Stopwatch objSw = new Stopwatch();

		private static readonly Comparison<ZDO> cmp = Cmp;

		private static ConfigEntry<bool> Mirror;

		private static ConfigEntry<bool> DgSpread;

		private static ConfigEntry<bool> LocSerialize;

		private static ConfigEntry<bool> ZoneThrottle;

		private static ConfigEntry<float> DgBudgetMs;

		private static ConfigEntry<float> LocBudgetMs;

		private static ConfigEntry<int> LocMaxDefer;

		private static ConfigEntry<int> ZoneMaxDefer;

		private static ConfigEntry<int> DgMaxLoads;

		private static ConfigEntry<bool> DgStagger;

		private static ConfigEntry<bool> LocHoldFrame;

		private static ConfigEntry<bool> LocPrewarm;

		private static ConfigEntry<float> ZoneHeavyMs;

		private static ConfigEntry<float> SettleGrace;

		private static ConfigEntry<bool> LocSplit;

		private static ConfigEntry<bool> LocSoftCap;

		private static ConfigEntry<bool> LocPreInst;

		private static ConfigEntry<bool> DgSoftEntry;

		private static ConfigEntry<bool> DistBacklogAware;

		private static ConfigEntry<float> LocHeavyMs;

		private static ConfigEntry<float> LocSoftCapMs;

		private static ConfigEntry<float> LocProxySoftMs;

		private static ConfigEntry<float> DgWallMs;

		private static ConfigEntry<int> LocHeavyPieces;

		private static ConfigEntry<int> NearBacklog;

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

		private static ConfigEntry<string> LocHeavyList;

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

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

		private static readonly HashSet<int> proxyHeavyHashes = new HashSet<int>();

		private static ManualLogSource BLog;

		private static Plugin inst;

		private static int locFrame = -1;

		private static int locDeferRun;

		private static int locCount;

		private static int sLocSpawns;

		private static int sLocDelayed;

		private static int sLocForced;

		private static double sLocMax;

		private static readonly Stopwatch locSw = new Stopwatch();

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

		private static int sLocHeld;

		private static int sLocPrewarm;

		private static bool inPrewarm;

		private static string sLocMaxName = "-";

		private static bool locSplitOk;

		private static bool locDeferNext;

		private static GameObject locAssetOff;

		private static int sLocSplit;

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

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

		private static int locPendingFrame;

		private static HashSet<string> heavyNames;

		private static string heavyListSeen;

		private static bool locAssetDisabled;

		private static int locCurHash;

		private static int sLocPreInst;

		private static int sLocPreInstUsed;

		private static int sLocPreInstSync;

		private static readonly Dictionary<int, AsyncInstantiateOperation<GameObject>> preOps = new Dictionary<int, AsyncInstantiateOperation<GameObject>>();

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

		private static readonly Vector3 prePark = new Vector3(0f, -9000f, 0f);

		private static readonly List<int> tmpPreKeys = new List<int>();

		private static int heavyJobFrame = -1;

		private static string heavyJobWhy = "-";

		private static int prevHfFrame = -1;

		private static string prevHfName = "-";

		private static string prevHfOwner = "-";

		private static double prevHfMs;

		private static int curHfFrame = -1;

		private static string curHfName = "-";

		private static string curHfOwner = "-";

		private static double curHfMs;

		private static readonly List<ZDO> rmDead = new List<ZDO>();

		private static int sRmPurged;

		private static FieldInfo fInst;

		private static ConfigEntry<bool> HyOn;

		private static ConfigEntry<float> HyKeepSec;

		private static ConfigEntry<float> HyCooldown;

		private static ConfigEntry<float> HyClose;

		private static ConfigEntry<int> HyExtra;

		private static readonly List<ZNetView> proxies = new List<ZNetView>();

		private static readonly HashSet<ZNetView> proxySet = new HashSet<ZNetView>();

		private static readonly Dictionary<ZDO, float> proxyOutSince = new Dictionary<ZDO, float>();

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

		private static readonly HashSet<ZDOID> proxySuppressedOnce = new HashSet<ZDOID>();

		private static int kaFrame = -1;

		private static readonly HashSet<ZDO> kaSeen = new HashSet<ZDO>();

		private static readonly HashSet<ZDO> keptNow = new HashSet<ZDO>();

		private static readonly Dictionary<Vector3Int, ZDOID> kaPos = new Dictionary<Vector3Int, ZDOID>();

		private static readonly List<string> kaNew = new List<string>();

		private static int sLocKept;

		private static int sLocKeptFrames;

		private static int sLocSuppressed;

		private static int sLocUnloaded;

		private static int sLocDup;

		private static FieldRef<LocationProxy, GameObject> lpInst;

		private static FieldRef<LocationProxy, bool> lpNeeds;

		private static MethodInfo getLoc;

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

		private static readonly int proxyHash = StringExtensionMethods.GetStableHashCode("LocationProxy");

		private static readonly HashSet<int> prewarmed = new HashSet<int>();

		private static float nextPrewarmClear;

		private static int zoneFrame = -1;

		private static int zoneDefer;

		private static int sZones;

		private static int sZoneDeferred;

		private static bool dgOk = true;

		private static int dgActive;

		private static int sDungeons;

		private static int sDgFrames;

		private static int sDgAborted;

		private static double sDgMaxFrame;

		private static MethodInfo placeRoom;

		private static MethodInfo release;

		private static MethodInfo dnsBegin;

		private static MethodInfo dnsEnd;

		private static int dgLoading;

		private static int sDgStaggered;

		private static double sDgStaggerWall;

		private static readonly FieldRef<DungeonGenerator, List<IReferenceCounted>> heldRefs = AccessTools.FieldRefAccess<DungeonGenerator, List<IReferenceCounted>>("m_heldReferences");

		private static readonly FieldRef<DungeonGenerator, ZDO> dgLoadingZdo = AccessTools.FieldRefAccess<DungeonGenerator, ZDO>("m_zdoSetToBeLoadingInZone");

		private static readonly FieldRef<DungeonGenerator, ZNetView> dgNview = AccessTools.FieldRefAccess<DungeonGenerator, ZNetView>("m_nview");

		private static Action<DungeonGenerator, AssetID, LoadResult> onRoomLoaded;

		private static FieldInfo fRd;

		private static FieldInfo fPos;

		private static FieldInfo fRot;

		private static readonly FieldRef<DungeonGenerator, int> roomsToLoad = AccessTools.FieldRefAccess<DungeonGenerator, int>("m_roomsToLoad");

		private static readonly FieldRef<DungeonGenerator, Array> loadedRooms = AccessTools.FieldRefAccess<DungeonGenerator, Array>("m_loadedRooms");

		private static readonly string[] Others = new string[3] { "com.Fire.FiresGhettoNetworkMod", "vpf.ValheimPerformanceFixes.RenderTrim", "com.Skarif.ValheimPerformanceOverhaul_WATER" };

		private static int phaseFrame = -1;

		private static bool phaseIn;

		private static float tailUntil;

		private static bool wasLoading = true;

		private static float settledSince = -1f;

		private static double prevSpawnMs;

		private static readonly Comparison<ZDO> cmpD = (ZDO x, ZDO y) => x.m_tempSortValue.CompareTo(y.m_tempSortValue);

		private static readonly Stopwatch coSw = Stopwatch.StartNew();

		private static readonly Stack<double> coStart = new Stack<double>();

		private static int coFrame = -1;

		private static double coFrameMs;

		private static int sLoadFrames;

		private static double sLoadMax;

		private static double sLoadTotal;

		private static int sLoadObjs;

		private static int sTails;

		private static int sTailFrames;

		private static int sTailHit;

		private static double sTailMax;

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

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

		private static Dictionary<Assembly, string> asmToMod;

		private static bool inLateRegen;

		private static bool inForce;

		private static int sRegenDeferred;

		private static readonly FieldRef<Heightmap, Mesh> collMesh = AccessTools.FieldRefAccess<Heightmap, Mesh>("m_collisionMesh");

		private static void UpdatePressure(float dt)
		{
			if (!(dt <= 0f))
			{
				float num = Mathf.Min(Time.unscaledDeltaTime, 0.25f);
				dtEma += (num - dtEma) * Mathf.Clamp01(dt / 1f);
				float num2 = AdFrameMs.Value / 1000f;
				float num3 = Mathf.Clamp01((dtEma - num2) / num2);
				float num4 = Mathf.Max(Mathf.Clamp01((float)lastPendNear / 150f), 0.7f * Mathf.Clamp01((float)lastPendDist / 600f));
				float num5 = Mathf.Clamp01((float)zoneDefer / 5f + (float)locDeferRun / 10f);
				int num6 = 0;
				try
				{
					num6 = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.NrOfInstances() : 0);
				}
				catch
				{
				}
				float num7 = Mathf.Clamp01(((float)num6 - 2000f) / 6000f);
				float num8 = Mathf.Clamp01(0.5f * num4 + 0.15f * num5 + 0.2f * num3 + 0.15f * num7);
				float value = SpawnBudgetPressure.Value;
				float num9 = ((num8 > value) ? AdRise.Value : AdFall.Value);
				SpawnBudgetPressure.Value = value + (num8 - value) * Mathf.Clamp01(dt / num9);
				SpawnBudgetPressure.Raw = num8;
				SpawnBudgetPressure.Instances = num6;
				SpawnBudgetPressure.PendingNear = lastPendNear;
				SpawnBudgetPressure.PendingDistant = lastPendDist;
			}
		}

		private static float AdT()
		{
			float value = AdLow.Value;
			float num = Mathf.Max(AdHigh.Value, value + 0.01f);
			return Mathf.Clamp01((SpawnBudgetPressure.Value - value) / (num - value)) * AdStr.Value;
		}

		private static void CountPendDist(List<ZDO> dist)
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime < nextPendCount)
			{
				return;
			}
			nextPendCount = unscaledTime + 0.5f;
			if (dist == null)
			{
				lastPendDist = 0;
				return;
			}
			float value = PendStale.Value;
			int num = 0;
			int num2 = 0;
			distSeen2.Clear();
			for (int i = 0; i < dist.Count; i++)
			{
				ZDO val = dist[i];
				if (val != null && !val.Created)
				{
					if (!distSeen.TryGetValue(val, out var value2))
					{
						value2 = unscaledTime;
					}
					distSeen2[val] = value2;
					if (value > 0f && unscaledTime - value2 > value)
					{
						num2++;
					}
					else
					{
						num++;
					}
				}
			}
			distSeen.Clear();
			foreach (KeyValuePair<ZDO, float> item in distSeen2)
			{
				distSeen[item.Key] = item.Value;
			}
			distSeen2.Clear();
			lastPendDist = num;
			lastPendDistStale = num2;
		}

		private static bool ArrivalOk()
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
				{
					return false;
				}
				Player localPlayer = Player.m_localPlayer;
				return (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && (Object)(object)ZoneSystem.instance != (Object)null;
			}
			catch
			{
				return false;
			}
		}

		private static void DmgPost(Character __instance, HitData hit)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && ((Object)(object)__instance == (Object)(object)localPlayer || (hit != null && (Object)(object)hit.GetAttacker() == (Object)(object)localPlayer)))
				{
					cmbLastHit = Time.unscaledTime;
				}
			}
			catch
			{
			}
		}

		private static double EffBudget()
		{
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			float unscaledTime = Time.unscaledTime;
			float num = Mathf.Clamp(unscaledTime - cmbLastT, 0f, 0.5f);
			cmbLastT = unscaledTime;
			UpdatePressure(num);
			double num2 = (AdOn.Value ? Mathf.Lerp(AdLen.Value, AdAgg.Value, AdT()) : BudgetMs.Value);
			SpawnBudgetPressure.BudgetMs = (float)num2;
			if (unscaledTime >= nextAdLog)
			{
				nextAdLog = unscaledTime + 30f;
				Log("ADAPTIVE pressure=" + SpawnBudgetPressure.Value.ToString("0.00") + " raw=" + SpawnBudgetPressure.Raw.ToString("0.00") + " pendNear=" + lastPendNear + " pendDist=" + lastPendDist + "(+" + lastPendDistStale + " stale) inst=" + SpawnBudgetPressure.Instances + " frame=" + (dtEma * 1000f).ToString("0.0") + "ms budget=" + num2.ToString("0.0") + "ms mode=" + (cmbIn ? "COMBAT" : "normal"));
			}
			if (!CmbOn.Value)
			{
				cmbIn = false;
				cmbF = 0f;
				return num2;
			}
			if (unscaledTime >= cmbNext)
			{
				cmbNext = unscaledTime + 0.25f;
				bool flag = unscaledTime - cmbLastHit < 1f;
				Player localPlayer = Player.m_localPlayer;
				if (!flag && (Object)(object)localPlayer != (Object)null)
				{
					Vector3 position = ((Component)localPlayer).transform.position;
					float num3 = CmbDetect.Value * CmbDetect.Value;
					foreach (Character allCharacter in Character.GetAllCharacters())
					{
						if ((Object)(object)allCharacter == (Object)null || allCharacter.IsPlayer() || allCharacter.IsTamed() || allCharacter.IsDead())
						{
							continue;
						}
						Vector3 val = ((Component)allCharacter).transform.position - position;
						if (!(((Vector3)(ref val)).sqrMagnitude > num3))
						{
							BaseAI baseAI = allCharacter.GetBaseAI();
							if ((Object)(object)baseAI != (Object)null && baseAI.IsAlerted() && BaseAI.IsEnemy(allCharacter, (Character)(object)localPlayer))
							{
								flag = true;
								break;
							}
						}
					}
				}
				bool flag2 = unscaledTime - cmbLastHit < 1f;
				bool flag3 = flag && !flag2;
				if (flag3)
				{
					if (cmbAiSince < 0f)
					{
						cmbAiSince = unscaledTime;
					}
				}
				else if (!flag2)
				{
					cmbAiSince = -1f;
				}
				bool flag4;
				if (cmbIn)
				{
					if (flag)
					{
						cmbLastSig = unscaledTime;
					}
					flag4 = (Object)(object)localPlayer != (Object)null && unscaledTime - cmbLastSig < CmbExit2.Value;
				}
				else
				{
					flag4 = (Object)(object)localPlayer != (Object)null && (flag2 || (flag3 && unscaledTime - cmbAiSince >= CmbEnterHold.Value));
					if (flag4)
					{
						cmbLastSig = unscaledTime;
						cmbWhy = (flag2 ? "damage" : ("alerted hostile for " + (unscaledTime - cmbAiSince).ToString("0.0") + "s"));
					}
				}
				if (flag4 != cmbIn)
				{
					cmbIn = flag4;
					if (flag4)
					{
						cmbEnterAt = unscaledTime;
						Log("combat mode ENTER (" + cmbWhy + ")");
					}
					else
					{
						Log("combat mode EXIT after " + (unscaledTime - cmbEnterAt).ToString("0") + "s (no signal for " + CmbExit2.Value.ToString("0") + "s)");
					}
				}
			}
			cmbF = Mathf.MoveTowards(cmbF, cmbIn ? 1f : 0f, num / CmbEase.Value);
			if (cmbF > 0f)
			{
				sCombatFrames++;
			}
			SpawnBudgetPressure.Combat = cmbIn;
			return Mathf.Lerp((float)num2, Mathf.Min((float)num2, CmbBudget.Value), cmbF);
		}

		private void Awake()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0d7c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d82: Expected O, but got Unknown
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Expected O, but got Unknown
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Expected O, but got Unknown
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Expected O, but got Unknown
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Expected O, but got Unknown
			//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f4: Expected O, but got Unknown
			//IL_0347: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Expected O, but got Unknown
			//IL_0378: Unknown result type (might be due to invalid IL or missing references)
			//IL_0382: Expected O, but got Unknown
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Expected O, but got Unknown
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fc: Expected O, but got Unknown
			//IL_042f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0439: Expected O, but got Unknown
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_049a: Expected O, but got Unknown
			//IL_0593: Unknown result type (might be due to invalid IL or missing references)
			//IL_059d: Expected O, but got Unknown
			//IL_05d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05da: Expected O, but got Unknown
			//IL_061b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0625: Expected O, but got Unknown
			//IL_0678: Unknown result type (might be due to invalid IL or missing references)
			//IL_0682: Expected O, but got Unknown
			//IL_06b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_06bf: Expected O, but got Unknown
			//IL_06f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_06fc: Expected O, but got Unknown
			//IL_072f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0739: Expected O, but got Unknown
			//IL_076c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0776: Expected O, but got Unknown
			//IL_07a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b3: Expected O, but got Unknown
			//IL_07e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_07f0: Expected O, but got Unknown
			//IL_0838: Unknown result type (might be due to invalid IL or missing references)
			//IL_0842: Expected O, but got Unknown
			//IL_0895: Unknown result type (might be due to invalid IL or missing references)
			//IL_089f: Expected O, but got Unknown
			//IL_08d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_08dc: Expected O, but got Unknown
			//IL_090f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0919: Expected O, but got Unknown
			//IL_094c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0956: Expected O, but got Unknown
			//IL_0b43: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b4d: Expected O, but got Unknown
			//IL_0b80: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b8a: Expected O, but got Unknown
			//IL_0bb6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bc0: Expected O, but got Unknown
			//IL_0bec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bf6: Expected O, but got Unknown
			//IL_0c29: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c33: Expected O, but got Unknown
			//IL_0c5b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c65: Expected O, but got Unknown
			//IL_0c98: Unknown result type (might be due to invalid IL or missing references)
			//IL_0ca2: Expected O, but got Unknown
			//IL_0cd5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0cdf: Expected O, but got Unknown
			//IL_0d12: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d1c: Expected O, but got Unknown
			//IL_0daa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0db7: Expected O, but got Unknown
			//IL_0e4f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0e55: Expected O, but got Unknown
			//IL_1216: Unknown result type (might be due to invalid IL or missing references)
			//IL_1222: Expected O, but got Unknown
			//IL_139e: Unknown result type (might be due to invalid IL or missing references)
			//IL_13a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_13ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_13c7: Expected O, but got Unknown
			//IL_13c7: Expected O, but got Unknown
			//IL_0f0d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f12: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f1d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f38: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f3d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f44: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f57: Expected O, but got Unknown
			//IL_0f57: Expected O, but got Unknown
			//IL_0fad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0fb2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0fc6: Expected O, but got Unknown
			//IL_0fee: Unknown result type (might be due to invalid IL or missing references)
			//IL_1005: Unknown result type (might be due to invalid IL or missing references)
			//IL_1010: Expected O, but got Unknown
			//IL_1010: Expected O, but got Unknown
			//IL_1083: Unknown result type (might be due to invalid IL or missing references)
			//IL_1098: Unknown result type (might be due to invalid IL or missing references)
			//IL_10a5: Expected O, but got Unknown
			//IL_10a5: Expected O, but got Unknown
			//IL_1119: Unknown result type (might be due to invalid IL or missing references)
			//IL_1126: Expected O, but got Unknown
			//IL_114e: Unknown result type (might be due to invalid IL or missing references)
			//IL_1163: Unknown result type (might be due to invalid IL or missing references)
			//IL_1179: Unknown result type (might be due to invalid IL or missing references)
			//IL_1184: Expected O, but got Unknown
			//IL_1184: Expected O, but got Unknown
			//IL_1184: Expected O, but got Unknown
			//IL_12e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_12eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_12ff: Expected O, but got Unknown
			//IL_11ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_11b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_11c5: Expected O, but got Unknown
			//IL_1696: Unknown result type (might be due to invalid IL or missing references)
			//IL_169b: Unknown result type (might be due to invalid IL or missing references)
			//IL_16ab: Expected O, but got Unknown
			//IL_16d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_16d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_16e8: Expected O, but got Unknown
			inst = this;
			BLog = base.Logger;
			if (Application.isBatchMode || (int)SystemInfo.graphicsDeviceType == 4)
			{
				base.Logger.LogInfo((object)"SpawnBudget 1.7.5: headless/server process detected, not patching anything.");
				return;
			}
			Mirror = base.Config.Bind<bool>("Spawn Budget - Diagnostics", "MirrorToLogOutput", true, "Also write patch status, ENGAGED and the STATS headline to BepInEx LogOutput.log (full detail stays in BepInEx/SpawnBudget.log).");
			DgSpread = base.Config.Bind<bool>("Spawn Budget - Dungeon", "SpreadRooms", false, "EXPERIMENTAL, off by default. Client only, world visible: place a dungeon's rooms over several frames (RoomBudgetMs per frame, >=1 room/frame) instead of all in one frame. Rooms are seeded per position, so the result is identical; the zone stays 'loading' until done (as vanilla during asset load). Disabled automatically if another mod patches DungeonGenerator.Spawn.");
			DgStagger = base.Config.Bind<bool>("Spawn Budget - Dungeon", "StaggerRoomLoads", true, "1.5.0: client, world visible: issue a dungeon's room prefab async loads a few unique assets at a time instead of all at once, so Unity integrates them on the main thread over several frames. Room list/positions come from the ZDO (unchanged); only load timing changes.");
			DgMaxLoads = base.Config.Bind<int>("Spawn Budget - Dungeon", "MaxRoomLoadsInFlight", 1, new ConfigDescription("1.7.5: unique room prefabs loading at the same time while staggering. Default 1 so SoftEntry crypt loads do not stack EarlyUpdate spikes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>()));
			LocHoldFrame = base.Config.Bind<bool>("Spawn Budget - Locations", "HoldOneFrameAfterLoad", true, "1.5.0: when a location's asset has just finished loading, delay its instantiate to the next frame so asset integration and instantiate do not land in the same frame.");
			LocPrewarm = base.Config.Bind<bool>("Spawn Budget - Locations", "PrewarmFromQueue", true, "1.5.0: request the location asset load (vanilla PokeCanSpawnLocation via ShouldDelayProxyLocationSpawning) as soon as a LocationProxy ZDO is in the pending creation queue, before the proxy is instantiated.");
			LocSplit = base.Config.Bind<bool>("Spawn Budget - Locations", "SplitHeavyLocationActivation", true, "1.5.2: client, settled in-game only: a heavy location is instantiated inactive in one frame and activated (Awake/OnEnable, renderers, SnapToGround) in the next frame, so its cost is spread over 2 frames. The next location is held until the activation is done.");
			LocHeavyMs = base.Config.Bind<float>("Spawn Budget - Locations", "HeavyLocationMs", 8f, "1.5.2: generic rule for ALL locations (vanilla and modded): a location whose last unsplit spawn took longer than this is treated as heavy (learned per session).");
			LocHeavyPieces = base.Config.Bind<int>("Spawn Budget - Locations", "HeavyLocationTransforms", 80, "1.7.5: a location prefab with more than this many child transforms is treated as heavy from its first spawn (0 = split every location). Lower = more POIs split across frames; default 80 covers more swamp/MWL buildings.");
			LocHeavyList = base.Config.Bind<string>("Spawn Budget - Locations", "HeavyLocations", "", "1.5.2: optional extra names (normally empty; the ms/transform rules already cover every location).");
			LocSoftCap = base.Config.Bind<bool>("Spawn Budget - Locations", "SoftCapHeavyActivation", true, "1.7.5: after a heavy location is instantiated inactive, activate it under SoftCapBudgetMs and defer SnapToGround to the next frame so activation does not hitch. Off = old 2-frame split only.");
			LocPreInst = base.Config.Bind<bool>("Spawn Budget - Locations", "PreInstantiateHeavy", true, "1.7.5: for heavy overworld LocationProxy locations, start Unity InstantiateAsync while approaching AND during arrival/teleport screens so SoftReferenceableAssets.Instantiate is not paid on the spawn frame. Spawn waits (up to MaxDeferFrames) for the clone.");
			LocSoftCapMs = base.Config.Bind<float>("Spawn Budget - Locations", "SoftCapBudgetMs", 10f, new ConfigDescription("1.7.5: per-frame ms budget for deferred heavy-location activation (Awake/OnEnable). SnapToGround runs on the following frame. Default 10.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 40f), Array.Empty<object>()));
			LocProxySoftMs = base.Config.Bind<float>("Spawn Budget - Locations", "SoftCapProxyCreateMs", 12f, new ConfigDescription("1.7.5: if creating a LocationProxy (or its location spawn) took longer than this many ms, SoftCap that location on the next activation path so Awake/SnapToGround do not stack on the same hitch.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 40f), Array.Empty<object>()));
			DgSoftEntry = base.Config.Bind<bool>("Spawn Budget - Dungeon", "SoftEntry", true, "1.7.4: when entering a crypt/dungeon, stagger room asset loads AND place rooms across frames with SoftEntryWallMs as a hard per-frame wall (~18 ms) so opening a crypt does not freeze for hundreds of ms. Starts as soon as the world is visible (loads before you can reach the next rooms). Independent of SpreadRooms; leave SpreadRooms off unless you want spread without SoftEntry.");
			DgWallMs = base.Config.Bind<float>("Spawn Budget - Dungeon", "SoftEntryWallMs", 18f, new ConfigDescription("1.7.4: hard per-frame wall (ms) for SoftEntry room loads and room placement. Aim: no dungeon wall above ~20 ms.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(6f, 40f), Array.Empty<object>()));
			DistBacklogAware = base.Config.Bind<bool>("Spawn Budget - General", "BacklogAwareDistantSkip", true, "1.7.4: also skip distant object creation (LocationProxy etc.) while many nearby objects are still waiting (NearBacklogThreshold), so nearby scenery fills in first.");
			NearBacklog = base.Config.Bind<int>("Spawn Budget - General", "NearBacklogThreshold", 40, new ConfigDescription("1.7.4: with BacklogAwareDistantSkip, skip distant creation while at least this many nearby objects are still waiting to appear.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 300), Array.Empty<object>()));
			HyOn = base.Config.Bind<bool>("Spawn Budget - Location Hysteresis", "Enabled", true, "1.7.2: client, settled in-game only: stop the unload/reload loop of locations at the rim of the loaded area when you move back and forth over a zone line (fights, kiting). A location that just left the area stays loaded for KeepLoadedSec if it is within KeepExtraZones of the rim, and one that was unloaded anyway is not re-created for ReloadCooldownSec unless it is within AlwaysReloadWithinMeters. Nearby objects are never affected.");
			HyKeepSec = base.Config.Bind<float>("Spawn Budget - Location Hysteresis", "KeepLoadedSec", 30f, new ConfigDescription("Seconds a location that left the loaded area is kept loaded before it is unloaded (0 = unload immediately, vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			HyExtra = base.Config.Bind<int>("Spawn Budget - Location Hysteresis", "KeepExtraZones", 1, new ConfigDescription("Only keep a location loaded while it is at most this many zones (64 m) beyond the vanilla loaded area.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3), Array.Empty<object>()));
			HyCooldown = base.Config.Bind<float>("Spawn Budget - Location Hysteresis", "ReloadCooldownSec", 10f, new ConfigDescription("After a location was unloaded, do not re-create it for this many seconds unless you are close to it (0 = off).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
			HyClose = base.Config.Bind<float>("Spawn Budget - Location Hysteresis", "AlwaysReloadWithinMeters", 128f, new ConfigDescription("A location within this distance (XZ) of you is always re-created immediately, cooldown or not.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			SettleGrace = base.Config.Bind<float>("Spawn Budget - General", "SettleGraceSec", 10f, new ConfigDescription("1.5.1: zone throttle / location hold+prewarm / room-load stagger stay vanilla until the player has spawned and the loading screen has been gone this long.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
			ZoneHeavyMs = base.Config.Bind<float>("Spawn Budget - Zones", "PostponeIfLastFrameOverMs", 40f, "1.5.0: postpone a client zone spawn if the previous frame took longer than this (or spent the object budget, or spawned a location).");
			DgBudgetMs = base.Config.Bind<float>("Spawn Budget - Dungeon", "RoomBudgetMs", 8f, new ConfigDescription("1.7.4: per-frame ms for dungeon room placement when SoftEntry/SpreadRooms is on. SoftEntry also clamps to SoftEntryWallMs.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f,