Decompiled source of ValheimTune v0.7.0

plugins/ValheimTune.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using ValheimTune.Patches;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ValheimTune")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.7.0.0")]
[assembly: AssemblyInformationalVersion("0.7.0+4d181ca723f884dfd05a2c443aacc630925d1ce3")]
[assembly: AssemblyProduct("ValheimTune")]
[assembly: AssemblyTitle("ValheimTune")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.7.0.0")]
[module: UnverifiableCode]
[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;
		}
	}
}
namespace ValheimTune
{
	public static class AssetUnload
	{
		public enum Decision
		{
			Run,
			Defer
		}

		public static Decision Decide(int peers, double deferredFor, double maxDeferSecs)
		{
			if (peers <= 0)
			{
				return Decision.Run;
			}
			if (deferredFor >= maxDeferSecs)
			{
				return Decision.Run;
			}
			return Decision.Defer;
		}
	}
	public static class Cfg
	{
		public static ConfigEntry<int> LogIntervalSeconds;

		public static ConfigEntry<int> SendWindowBytes;

		public static ConfigEntry<int> MinHeadroomBytes;

		public static ConfigEntry<bool> AllPeersPerRound;

		public static ConfigEntry<float> RoundSeconds;

		public static ConfigEntry<int> SendRateMax;

		public static ConfigEntry<int> SendRateMin;

		public static ConfigEntry<int> MaxPacketsPerPeerPerFrame;

		public static ConfigEntry<int> TargetFrameRate;

		public static ConfigEntry<int> ConfigReloadSeconds;

		public static ConfigEntry<bool> FloatingDropsRun;

		public static ConfigEntry<bool> FloatingDropsDelete;

		public static ConfigEntry<string> HotObjectsIgnore;

		public static ConfigEntry<bool> DirtySets;

		public static ConfigEntry<float> ReconcileSeconds;

		public static ConfigEntry<int> RelayMinIntervalMs;

		public static ConfigEntry<string> KnownGoodBuilds;

		public static ConfigEntry<bool> DisableOnUnknownBuild;

		public static ConfigEntry<bool> TopKSort;

		public static ConfigEntry<int> TopK;

		public static ConfigEntry<bool> ServerSkipRenderMesh;

		public static ConfigEntry<bool> DeferAssetUnload;

		public static ConfigEntry<int> AssetUnloadMaxDeferMinutes;

		public static void Bind(ConfigFile c)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d3: Expected O, but got Unknown
			//IL_02f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Expected O, but got Unknown
			LogIntervalSeconds = c.Bind<int>("Measure", "LogIntervalSeconds", 10, "How often to print the stats line. 0 disables.");
			ConfigReloadSeconds = c.Bind<int>("Measure", "ConfigReloadSeconds", 5, "Re-read the cfg file this often so knobs that are read at runtime change without a restart. 0 disables. Patch-time settings (SendZDOs window, Steam send rate) still need a restart.");
			SendWindowBytes = c.Bind<int>("Sync", "SendWindowBytes", 10240, new ConfigDescription("Per-peer bytes in flight before the server stops queueing ZDO data. Vanilla 10240. Try 32768. Also sizes each iteration of the disconnect flush.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(4096, 1048576), Array.Empty<object>()));
			MinHeadroomBytes = c.Bind<int>("Sync", "MinHeadroomBytes", 2048, new ConfigDescription("Below this much free window the peer is skipped this round. Vanilla 2048. Scale with the window; must stay below SendWindowBytes.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(256, 262144), Array.Empty<object>()));
			AllPeersPerRound = c.Bind<bool>("Sync", "AllPeersPerRound", false, "Serve every peer each round instead of one peer per frame. Turn on after B1 is verified with several players; otherwise it concentrates the scan cost into one frame (6 peers x 4.1 ms full scans per 0.05 s if the DirtySets watchdog ever disables dirty sets).");
			RoundSeconds = c.Bind<float>("Sync", "RoundSeconds", 0.05f, new ConfigDescription("Round period when AllPeersPerRound is on. Vanilla 0.05.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
			SendRateMax = c.Bind<int>("Steam", "SendRateMaxBytesPerSec", 153600, "Steam per-connection send rate cap. Vanilla 153600 (150 KB/s). Try 1048576. Bounded by your upload / player count.");
			SendRateMin = c.Bind<int>("Steam", "SendRateMinBytesPerSec", 153600, "Leave at vanilla so the estimator can back off on loss.");
			MaxPacketsPerPeerPerFrame = c.Bind<int>("Receive", "MaxPacketsPerPeerPerFrame", 0, "Stop draining one peer's socket after this many packets in a frame; the rest wait in Steam's queue. 0 = vanilla (unlimited). Try 64.");
			TargetFrameRate = c.Bind<int>("Server", "TargetFrameRate", 0, "Application.targetFrameRate for the headless server. 0 = do not touch. Round period is 0.05 + players/fps, so 30 -> 60 doubles the sync rate at 6 players. Costs CPU.");
			FloatingDropsRun = c.Bind<bool>("Cleanup", "FloatingDropsRun", false, "One-shot trigger: set true to scan for item drops floating in water. The plugin runs it on the next config reload and sets this back to false. Measured ~50 ms of main-thread stall on a 698k-ZDO world (one 67 ms frame, 2026-09-07).");
			FloatingDropsDelete = c.Bind<bool>("Cleanup", "FloatingDropsDelete", false, "When a scan runs with this true, the found items are DELETED (server takes ownership and destroys them; clients see them vanish). Leave false for a dry run that only logs counts.");
			HotObjectsIgnore = c.Bind<string>("Measure", "HotObjectsIgnore", "Player,Fish1,Fish2,Fish3", "Comma-separated prefab names left out of the hot-objects line (things that are expected to move).");
			DirtySets = c.Bind<bool>("Sync", "DirtySets", true, "B1: only consider changed ZDOs each round instead of rescanning the whole active area. Full scan on join, zone change, and every ReconcileSeconds.");
			ReconcileSeconds = c.Bind<float>("Sync", "ReconcileSeconds", 30f, "Safety-net full scan interval per peer when DirtySets is on.");
			RelayMinIntervalMs = c.Bind<int>("Sync", "RelayMinIntervalMs", 0, "Do not re-send a non-prioritized object (fish, items, pieces) to the same peer more often than this, in ms. 0 = vanilla. 200 = 5 Hz; fish and drifting items are the bulk of idle traffic at a big base.");
			KnownGoodBuilds = c.Bind<string>("Compat", "KnownGoodBuilds", "1.0.7", "Game versions (Version.CurrentVersion) this build of the plugin was verified against. Comma-separated.");
			DisableOnUnknownBuild = c.Bind<bool>("Compat", "DisableOnUnknownBuild", true, "On a version not in KnownGoodBuilds, keep only measurement, the send-rate postfix and the constant swap; every replacement patch runs vanilla.");
			TopKSort = c.Bind<bool>("Sync", "TopKSort", true, "B2: bounded-heap selection of the objects that fit the send window instead of a full sort of every candidate. Only matters during joins, zone changes and the reconcile scan.");
			ServerSkipRenderMesh = c.Bind<bool>("Server", "SkipRenderMesh", false, "B4a: skip Heightmap.RebuildRenderMesh on a dedicated server. The render mesh is rebuilt for every ghost zone a player explores and every terrain edit that loads with one, and never drawn on a -nographics process. Collision mesh untouched. Watch meshSkips on the stats line.");
			DeferAssetUnload = c.Bind<bool>("Server", "DeferAssetUnload", false, "G1: hold the hourly Resources.UnloadUnusedAssets() until no players are connected. Vanilla runs it every hour regardless (Game.cs:239); measured 443-607 ms of main-thread stall on a 698k-ZDO world, landing with players online. Deferred, not skipped: it still runs the moment the server empties, or after AssetUnloadMaxDeferMinutes whichever comes first.");
			AssetUnloadMaxDeferMinutes = c.Bind<int>("Server", "AssetUnloadMaxDeferMinutes", 240, new ConfigDescription("Backstop for DeferAssetUnload: run the collection even with players online once it has been held this long. Stops a server that never empties from never collecting.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 1440), Array.Empty<object>()));
			TopK = c.Bind<int>("Sync", "TopK", 0, new ConfigDescription("How many candidates to order per round. 0 = SendWindowBytes / 64 (512 at 32 KB), never below 64.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 8192), Array.Empty<object>()));
		}
	}
	public sealed class ChurnTally
	{
		private readonly Dictionary<int, int> _counts = new Dictionary<int, int>();

		public int Total { get; private set; }

		public void Add(int key)
		{
			_counts.TryGetValue(key, out var value);
			_counts[key] = value + 1;
			Total++;
		}

		public List<KeyValuePair<int, int>> Top(int n)
		{
			return _counts.OrderByDescending((KeyValuePair<int, int> kv) => kv.Value).Take(n).ToList();
		}

		public void Reset()
		{
			_counts.Clear();
			Total = 0;
		}
	}
	public static class Compat
	{
		public static bool ReplacementsAllowed = true;

		public static string GameVersion = "?";

		public static bool IsKnown(string version, string knownList)
		{
			if (string.IsNullOrEmpty(knownList))
			{
				return false;
			}
			return knownList.Split(',').Any((string s) => s.Trim() == version);
		}
	}
	public static class ConstSwap
	{
		public static int LastReplaced;

		public static IEnumerable<CodeInstruction> Replace(IEnumerable<CodeInstruction> code, IReadOnlyDictionary<int, int> map)
		{
			LastReplaced = 0;
			foreach (CodeInstruction item in code)
			{
				if (item.opcode == OpCodes.Ldc_I4 && item.operand is int key && map.TryGetValue(key, out var value))
				{
					LastReplaced++;
					yield return new CodeInstruction(OpCodes.Ldc_I4, (object)value)
					{
						labels = item.labels,
						blocks = item.blocks
					};
				}
				else
				{
					yield return item;
				}
			}
		}
	}
	public sealed class DirtyPeerState<TId>
	{
		private (int x, int y) _lastZone = (x: int.MinValue, y: int.MinValue);

		private float _lastFullScan = float.NegativeInfinity;

		private bool _wasActive;

		private readonly List<TId> _prune = new List<TId>();

		public HashSet<TId> Pending { get; } = new HashSet<TId>();

		public int FullScans { get; private set; }

		public bool NeedsFullScan((int x, int y) zone, float now, float reconcileSeconds, bool active)
		{
			int num;
			if (_wasActive)
			{
				(int, int) tuple = zone;
				(int, int) lastZone = _lastZone;
				if (tuple.Item1 == lastZone.Item1 && tuple.Item2 == lastZone.Item2)
				{
					num = ((now - _lastFullScan > reconcileSeconds) ? 1 : 0);
					goto IL_003b;
				}
			}
			num = 1;
			goto IL_003b;
			IL_003b:
			_wasActive = active;
			if (num != 0)
			{
				_lastZone = zone;
				_lastFullScan = now;
				FullScans++;
			}
			return (byte)num != 0;
		}

		public void Drain(List<TId> into, Func<TId, bool> exists, Func<TId, bool> inArea, Func<TId, bool> shouldSend, Func<TId, bool> deferSend = null)
		{
			_prune.Clear();
			foreach (TId item in Pending)
			{
				if (!exists(item) || !inArea(item) || !shouldSend(item))
				{
					_prune.Add(item);
				}
				else if (deferSend == null || !deferSend(item))
				{
					into.Add(item);
				}
			}
			foreach (TId item2 in _prune)
			{
				Pending.Remove(item2);
			}
		}
	}
	public static class FloatingDrops
	{
		private static readonly Dictionary<int, bool> s_isCandidate = new Dictionary<int, bool>();

		private static bool IsFloatingCandidate(int hash)
		{
			if (s_isCandidate.TryGetValue(hash, out var value))
			{
				return value;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(hash) : null);
			value = (Object)(object)val != (Object)null && ((Object)(object)val.GetComponent<ItemDrop>() != (Object)null || (Object)(object)val.GetComponent<TreeLog>() != (Object)null) && (Object)(object)val.GetComponent<Fish>() == (Object)null;
			s_isCandidate[hash] = value;
			return value;
		}

		public static (int found, int deleted) Run(bool delete, ManualLogSource log)
		{
			//IL_0092: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			if (instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || WorldGenerator.instance == null)
			{
				log.LogWarning((object)"[ValheimTune] floating drops: world not ready");
				return (found: 0, deleted: 0);
			}
			float waterLevel = ZoneSystem.instance.m_waterLevel;
			List<ZDOID> list = new List<ZDOID>();
			ChurnTally churnTally = new ChurnTally();
			List<(string, Vector3)> list2 = new List<(string, Vector3)>();
			foreach (KeyValuePair<ZDOID, ZDO> item in instance.m_objectsByID)
			{
				ZDO value = item.Value;
				if (!IsFloatingCandidate(value.GetPrefab()))
				{
					continue;
				}
				Vector3 position = value.GetPosition();
				if (!(position.y > waterLevel + 0.25f) && !(WorldGenerator.instance.GetHeight(position.x, position.z) >= waterLevel))
				{
					list.Add(item.Key);
					churnTally.Add(value.GetPrefab());
					if (list2.Count < 10)
					{
						GameObject prefab = ZNetScene.instance.GetPrefab(value.GetPrefab());
						list2.Add((((Object)(object)prefab != (Object)null) ? ((Object)prefab).name : value.GetPrefab().ToString(), position));
					}
				}
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("[ValheimTune] floating drops: ").Append(list.Count).Append(delete ? " found, deleting:" : " found (dry run):");
			foreach (KeyValuePair<int, int> item2 in churnTally.Top(10))
			{
				GameObject prefab2 = ZNetScene.instance.GetPrefab(item2.Key);
				stringBuilder.Append(' ').Append(((Object)(object)prefab2 != (Object)null) ? ((Object)prefab2).name : item2.Key.ToString()).Append('=')
					.Append(item2.Value);
			}
			log.LogInfo((object)stringBuilder.ToString());
			if (list2.Count > 0)
			{
				StringBuilder stringBuilder2 = new StringBuilder("[ValheimTune] floating drops sample:");
				foreach (var item3 in list2)
				{
					stringBuilder2.Append(' ').Append(item3.Item1).Append('@')
						.Append('(')
						.Append((int)item3.Item2.x)
						.Append(',')
						.Append((int)item3.Item2.y)
						.Append(',')
						.Append((int)item3.Item2.z)
						.Append(')');
				}
				log.LogInfo((object)stringBuilder2.ToString());
			}
			int num = 0;
			if (delete)
			{
				long sessionID = ZDOMan.GetSessionID();
				foreach (ZDOID item4 in list)
				{
					ZDO zDO = instance.GetZDO(item4);
					if (zDO != null)
					{
						zDO.SetOwner(sessionID);
						instance.DestroyZDO(zDO);
						num++;
					}
				}
				log.LogInfo((object)$"[ValheimTune] floating drops: {num} queued for destruction");
			}
			return (found: list.Count, deleted: num);
		}
	}
	[BepInPlugin("akoozie.valheimtune", "ValheimTune", "0.7.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "akoozie.valheimtune";

		public const string Version = "0.7.0";

		private const float WatchdogWindowSeconds = 10f;

		public static ManualLogSource Log;

		public static Plugin Instance;

		private Harmony _harmony;

		private float _logTimer;

		private float _reloadTimer;

		private float _wdTimer;

		private void Awake()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			if (Cfg.MinHeadroomBytes.Value >= Cfg.SendWindowBytes.Value)
			{
				Log.LogError((object)"[ValheimTune] MinHeadroomBytes >= SendWindowBytes: no ZDO will ever be sent. Fix the config.");
			}
			Compat.GameVersion = ((object)Version.CurrentVersion/*cast due to .constrained prefix*/).ToString();
			Compat.ReplacementsAllowed = !Cfg.DisableOnUnknownBuild.Value || Compat.IsKnown(Compat.GameVersion, Cfg.KnownGoodBuilds.Value);
			if (!Compat.ReplacementsAllowed)
			{
				Log.LogWarning((object)("[ValheimTune] game " + Compat.GameVersion + " not in KnownGoodBuilds (" + Cfg.KnownGoodBuilds.Value + "): replacement patches inactive, running vanilla + measurement"));
			}
			_harmony = new Harmony("akoozie.valheimtune");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			uint num = 39u;
			Log.LogInfo((object)string.Format("[ValheimTune] {0} loaded on game {1} (net {2}), {3} methods patched, replacements {4}", "0.7.0", Compat.GameVersion, num, _harmony.GetPatchedMethods().Count(), Compat.ReplacementsAllowed ? "on" : "OFF"));
		}

		private void Update()
		{
			//IL_04ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0513: Unknown result type (might be due to invalid IL or missing references)
			ReloadConfig();
			RunFloatingDropsIfRequested();
			AssetUnloadPatch.RunIfDue();
			MeasurePatches.FrameMs.Add(Time.unscaledDeltaTime * 1000f);
			int value = Cfg.TargetFrameRate.Value;
			if (value > 0 && Application.targetFrameRate != value)
			{
				Application.targetFrameRate = value;
				Log.LogInfo((object)$"[ValheimTune] targetFrameRate set to {value}");
			}
			ZDOMan instance = ZDOMan.instance;
			int num = ((instance != null) ? instance.GetRecvZDOs() : 0);
			_wdTimer += Time.unscaledDeltaTime;
			if (_wdTimer >= 10f)
			{
				long watchdogRecv = DirtyPatches.WatchdogRecv;
				long watchdogMarks = DirtyPatches.WatchdogMarks;
				if (DirtyPatches.WatchdogShouldTrip(Cfg.DirtySets.Value, DirtyPatches.Disabled, watchdogRecv > 0, watchdogMarks))
				{
					DirtyPatches.Disabled = true;
					Log.LogError((object)$"[ValheimTune] DirtySets: {watchdogRecv} ZDOs deserialized in {10f:F0} s but the revision hook fired 0 times; falling back to vanilla scanning. The ZDO revision setters are probably inlined on this Mono build.");
				}
				else if (DirtyPatches.WatchdogShouldRearm(DirtyPatches.Disabled, watchdogMarks))
				{
					DirtyPatches.Disabled = false;
					Log.LogWarning((object)$"[ValheimTune] DirtySets: revision hook fired {watchdogMarks} times while disabled; re-armed (full scan per peer on the next round).");
				}
				DirtyPatches.WatchdogMarks = 0L;
				DirtyPatches.WatchdogRecv = 0L;
				_wdTimer = 0f;
			}
			int value2 = Cfg.LogIntervalSeconds.Value;
			if (value2 <= 0)
			{
				MeasurePatches.ResetAll();
				DirtyPatches.ResetCounters();
				RenderMeshPatch.Skipped = 0L;
				return;
			}
			_logTimer += Time.unscaledDeltaTime;
			if (_logTimer < (float)value2)
			{
				return;
			}
			_logTimer = 0f;
			int num2 = instance?.m_peers.Count ?? 0;
			int num3 = ((instance != null) ? instance.GetSentZDOs() : 0);
			int num4 = num;
			Log.LogInfo((object)($"[ValheimTune] frame avg {MeasurePatches.FrameMs.Avg:F1} max {MeasurePatches.FrameMs.Max:F1} ms " + $"({1000.0 / Math.Max(0.01, MeasurePatches.FrameMs.Avg):F0} fps) | " + $"syncList avg {MeasurePatches.SyncListMs.Avg:F2} max {MeasurePatches.SyncListMs.Max:F2} ms | " + $"send avg {MeasurePatches.SendMs.Avg:F2} ms | Z max {MeasurePatches.MaxZ} | peer-sends {MeasurePatches.Rounds} | " + $"zdos/s sent {num3} recv {num4} | peers {num2}" + string.Format(" | marks {0} full {1} dirtyRounds {2} deferred {3} drained {4}{5}", DirtyPatches.Marks, DirtyPatches.FullScans, DirtyPatches.DirtyRounds, DirtyPatches.Deferred, DirtyPatches.LastDrained, DirtyPatches.Disabled ? " DISABLED" : "") + $" | meshSkips {RenderMeshPatch.Skipped}"));
			if (MeasurePatches.Recv.Total > 0)
			{
				StringBuilder stringBuilder = new StringBuilder("[ValheimTune] recv by prefab (");
				stringBuilder.Append(MeasurePatches.Recv.Total).Append(" in window):");
				ZNetScene scene = ZNetScene.instance;
				foreach (KeyValuePair<int, int> item2 in MeasurePatches.Recv.Top(8))
				{
					string text = null;
					if ((Object)(object)scene != (Object)null)
					{
						GameObject prefab = scene.GetPrefab(item2.Key);
						if ((Object)(object)prefab != (Object)null)
						{
							text = ((Object)prefab).name;
						}
					}
					stringBuilder.Append(' ').Append(text ?? item2.Key.ToString()).Append('=')
						.Append(item2.Value);
				}
				Log.LogInfo((object)stringBuilder.ToString());
				HashSet<string> ignoreSet = new HashSet<string>(from s in Cfg.HotObjectsIgnore.Value.Split(',')
					select s.Trim());
				List<KeyValuePair<ZDOID, (int, int, Vector3)>> list = MeasurePatches.HotTop(12).Where(delegate(KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)> x)
				{
					ZNetScene obj3 = scene;
					object obj4;
					if (obj3 == null)
					{
						obj4 = null;
					}
					else
					{
						GameObject prefab3 = obj3.GetPrefab(x.Value.prefab);
						obj4 = ((prefab3 != null) ? ((Object)prefab3).name : null);
					}
					if (obj4 == null)
					{
						obj4 = x.Value.prefab.ToString();
					}
					string item = (string)obj4;
					return !ignoreSet.Contains(item);
				}).Take(6)
					.ToList();
				if (list.Count > 0)
				{
					StringBuilder stringBuilder2 = new StringBuilder("[ValheimTune] hot objects:");
					foreach (KeyValuePair<ZDOID, (int, int, Vector3)> item3 in list)
					{
						ZNetScene obj = scene;
						object obj2;
						if (obj == null)
						{
							obj2 = null;
						}
						else
						{
							GameObject prefab2 = obj.GetPrefab(item3.Value.Item2);
							obj2 = ((prefab2 != null) ? ((Object)prefab2).name : null);
						}
						if (obj2 == null)
						{
							obj2 = item3.Value.Item2.ToString();
						}
						string value3 = (string)obj2;
						int value4 = (int)item3.Value.Item3.x;
						int value5 = (int)item3.Value.Item3.z;
						stringBuilder2.Append(' ').Append(value3).Append('=')
							.Append(item3.Value.Item1)
							.Append('@')
							.Append('(')
							.Append(value4)
							.Append(',')
							.Append(value5)
							.Append(')');
					}
					Log.LogInfo((object)stringBuilder2.ToString());
				}
			}
			RenderMeshPatch.Skipped = 0L;
			DirtyPatches.ResetCounters();
			MeasurePatches.ResetAll();
		}

		private void ReloadConfig()
		{
			int value = Cfg.ConfigReloadSeconds.Value;
			if (value <= 0)
			{
				return;
			}
			_reloadTimer += Time.unscaledDeltaTime;
			if (_reloadTimer < (float)value)
			{
				return;
			}
			_reloadTimer = 0f;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[ValheimTune] config reload failed: {arg}");
			}
		}

		private void RunFloatingDropsIfRequested()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || !Cfg.FloatingDropsRun.Value)
			{
				return;
			}
			try
			{
				FloatingDrops.Run(Cfg.FloatingDropsDelete.Value, Log);
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[ValheimTune] floating drops run failed: {arg}");
			}
			finally
			{
				Cfg.FloatingDropsRun.Value = false;
				((BaseUnityPlugin)this).Config.Save();
			}
		}
	}
	public sealed class RollingStats
	{
		private double _sum;

		public int Count { get; private set; }

		public double Max { get; private set; }

		public double Avg
		{
			get
			{
				if (Count != 0)
				{
					return _sum / (double)Count;
				}
				return 0.0;
			}
		}

		public void Add(double v)
		{
			_sum += v;
			Count++;
			if (v > Max)
			{
				Max = v;
			}
		}

		public void Reset()
		{
			_sum = 0.0;
			Count = 0;
			Max = 0.0;
		}
	}
	public static class TopK
	{
		private static class Buf<T>
		{
			public static T[] Items;
		}

		private static double[] s_rank;

		private static int[] s_heap;

		private static bool[] s_taken;

		public static void Select<T>(List<T> items, Func<T, double> rank, int k)
		{
			int count = items.Count;
			if (count == 0 || k <= 0)
			{
				return;
			}
			if (k > count)
			{
				k = count;
			}
			if (s_rank == null || s_rank.Length < count)
			{
				s_rank = new double[count];
				s_heap = new int[count];
				s_taken = new bool[count];
			}
			if (Buf<T>.Items == null || Buf<T>.Items.Length < count)
			{
				Buf<T>.Items = new T[count];
			}
			double[] array = s_rank;
			int[] array2 = s_heap;
			bool[] array3 = s_taken;
			T[] items2 = Buf<T>.Items;
			for (int i = 0; i < count; i++)
			{
				items2[i] = items[i];
				array[i] = rank(items[i]);
				array3[i] = false;
			}
			int num = 0;
			for (int j = 0; j < count; j++)
			{
				if (num < k)
				{
					array2[num] = j;
					num++;
					SiftUp(array2, array, num - 1);
				}
				else if (array[j] < array[array2[0]])
				{
					array2[0] = j;
					SiftDown(array2, array, 0, num);
				}
			}
			int num2 = num;
			for (int num3 = num2 - 1; num3 >= 0; num3--)
			{
				int num4 = array2[0];
				array3[num4] = true;
				items[num3] = items2[num4];
				num--;
				array2[0] = array2[num];
				SiftDown(array2, array, 0, num);
			}
			int num5 = num2;
			for (int l = 0; l < count; l++)
			{
				if (!array3[l])
				{
					items[num5++] = items2[l];
				}
			}
		}

		private static void SiftUp(int[] heap, double[] rankArr, int i)
		{
			while (i > 0)
			{
				int num = (i - 1) / 2;
				if (!(rankArr[heap[num]] >= rankArr[heap[i]]))
				{
					ref int reference = ref heap[num];
					ref int reference2 = ref heap[i];
					int num2 = heap[i];
					int num3 = heap[num];
					reference = num2;
					reference2 = num3;
					i = num;
					continue;
				}
				break;
			}
		}

		private static void SiftDown(int[] heap, double[] rankArr, int i, int count)
		{
			while (true)
			{
				int num = 2 * i + 1;
				int num2 = 2 * i + 2;
				int num3 = i;
				if (num < count && rankArr[heap[num]] > rankArr[heap[num3]])
				{
					num3 = num;
				}
				if (num2 < count && rankArr[heap[num2]] > rankArr[heap[num3]])
				{
					num3 = num2;
				}
				if (num3 != i)
				{
					ref int reference = ref heap[i];
					ref int reference2 = ref heap[num3];
					int num4 = heap[num3];
					int num5 = heap[i];
					reference = num4;
					reference2 = num5;
					i = num3;
					continue;
				}
				break;
			}
		}
	}
}
namespace ValheimTune.Patches
{
	[HarmonyPatch(typeof(Game), "CollectResources")]
	public static class AssetUnloadPatch
	{
		private static float s_deferredSince = -1f;

		public static bool Pending => s_deferredSince >= 0f;

		[HarmonyPrefix]
		private static bool Prefix()
		{
			if (!Compat.ReplacementsAllowed || !Cfg.DeferAssetUnload.Value)
			{
				return true;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated())
			{
				return true;
			}
			int peerConnections = ZNet.instance.GetPeerConnections();
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			double num = ((s_deferredSince < 0f) ? 0.0 : ((double)(realtimeSinceStartup - s_deferredSince)));
			double maxDeferSecs = (double)Cfg.AssetUnloadMaxDeferMinutes.Value * 60.0;
			if (AssetUnload.Decide(peerConnections, num, maxDeferSecs) == AssetUnload.Decision.Defer)
			{
				if (s_deferredSince < 0f)
				{
					s_deferredSince = realtimeSinceStartup;
					Plugin.Log.LogInfo((object)$"[ValheimTune] asset unload deferred: {peerConnections} player(s) online, will run when the server empties (backstop {Cfg.AssetUnloadMaxDeferMinutes.Value} min)");
				}
				return false;
			}
			if (s_deferredSince >= 0f)
			{
				Plugin.Log.LogInfo((object)$"[ValheimTune] asset unload running after {num:F0} s deferred ({peerConnections} player(s) online)");
				s_deferredSince = -1f;
			}
			return true;
		}

		public static void RunIfDue()
		{
			if (!Pending || !Cfg.DeferAssetUnload.Value)
			{
				return;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			double deferredFor = Time.realtimeSinceStartup - s_deferredSince;
			if (AssetUnload.Decide(instance.GetPeerConnections(), deferredFor, (double)Cfg.AssetUnloadMaxDeferMinutes.Value * 60.0) == AssetUnload.Decision.Run)
			{
				Game instance2 = Game.instance;
				if (instance2 != null)
				{
					instance2.CollectResources(false);
				}
			}
		}
	}
	[HarmonyPatch]
	public static class ConstPatches
	{
		private static bool s_allPeersDisabledLogged;

		private static bool s_rateLogged;

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> SendZDOsWindow(IEnumerable<CodeInstruction> code)
		{
			Dictionary<int, int> map = new Dictionary<int, int>
			{
				{
					10240,
					Cfg.SendWindowBytes.Value
				},
				{
					2048,
					Cfg.MinHeadroomBytes.Value
				}
			};
			List<CodeInstruction> list = new List<CodeInstruction>(code);
			List<CodeInstruction> result = new List<CodeInstruction>(ConstSwap.Replace(list, map));
			if (ConstSwap.LastReplaced != 3)
			{
				Plugin.Log.LogError((object)$"[ValheimTune] SendZDOs: {ConstSwap.LastReplaced} constants replaced, expected 3; leaving the method untouched");
				return list;
			}
			Plugin.Log.LogInfo((object)$"[ValheimTune] SendZDOs window {Cfg.SendWindowBytes.Value}/{Cfg.MinHeadroomBytes.Value}, {ConstSwap.LastReplaced} constants replaced (expected 3)");
			return result;
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")]
		[HarmonyPrefix]
		private static bool AllPeersPerRound(ZDOMan __instance, float dt)
		{
			if (!Compat.ReplacementsAllowed || !Cfg.AllPeersPerRound.Value || DirtyPatches.Disabled)
			{
				if (DirtyPatches.Disabled && !s_allPeersDisabledLogged)
				{
					s_allPeersDisabledLogged = true;
					Plugin.Log.LogWarning((object)"[ValheimTune] AllPeersPerRound skipped: DirtySets watchdog disabled dirty sets, falling back to vanilla one-peer-per-frame to avoid stacking full scans in one frame.");
				}
				return true;
			}
			if (__instance.m_peers.Count == 0)
			{
				return false;
			}
			__instance.m_sendTimer += dt;
			if (__instance.m_sendTimer < Cfg.RoundSeconds.Value)
			{
				return false;
			}
			__instance.m_sendTimer = 0f;
			for (int i = 0; i < __instance.m_peers.Count; i++)
			{
				try
				{
					__instance.SendZDOs(__instance.m_peers[i], false);
				}
				catch (Exception arg)
				{
					Plugin.Log.LogError((object)$"[ValheimTune] SendZDOs threw for peer {i}: {arg}");
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
		[HarmonyPostfix]
		private static void SendRate()
		{
			SetGlobalInt((ESteamNetworkingConfigValue)10, Cfg.SendRateMin.Value);
			SetGlobalInt((ESteamNetworkingConfigValue)11, Cfg.SendRateMax.Value);
			if (!s_rateLogged)
			{
				s_rateLogged = true;
				Plugin.Log.LogInfo((object)$"[ValheimTune] Steam send rate min {Cfg.SendRateMin.Value} max {Cfg.SendRateMax.Value} B/s");
			}
		}

		private static void SetGlobalInt(ESteamNetworkingConfigValue key, int value)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
			try
			{
				if (!SteamGameServerNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject()))
				{
					Plugin.Log.LogError((object)$"[ValheimTune] Steam rejected {key} = {value}");
				}
			}
			finally
			{
				gCHandle.Free();
			}
		}
	}
	[HarmonyPatch]
	public static class DirtyPatches
	{
		public static long MarksData;

		public static long MarksOwner;

		public static int FullScans;

		public static int DirtyRounds;

		public static int Deferred;

		public static int LastDrained;

		public static bool Disabled;

		public static long WatchdogMarks;

		public static long WatchdogRecv;

		private static readonly ConditionalWeakTable<ZDOPeer, DirtyPeerState<ZDOID>> s_state = new ConditionalWeakTable<ZDOPeer, DirtyPeerState<ZDOID>>();

		private static readonly List<ZDOID> s_ids = new List<ZDOID>(256);

		private static int s_relayMinMs;

		public static long Marks => MarksData + MarksOwner;

		private static bool Active
		{
			get
			{
				if (Compat.ReplacementsAllowed && Cfg.DirtySets.Value && !Disabled && (Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static int ZoneChebyshev(Vector2s a, Vector2s b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			int num = a.x - b.x;
			if (num < 0)
			{
				num = -num;
			}
			int num2 = a.y - b.y;
			if (num2 < 0)
			{
				num2 = -num2;
			}
			if (num <= num2)
			{
				return num2;
			}
			return num;
		}

		internal static bool InNear(Vector2s centre, Vector2s candidate, SimulationDistance sd)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (ZoneChebyshev(centre, candidate) > ((SimulationDistance)(ref sd)).NearSimulationDistance)
			{
				return false;
			}
			if (!((SimulationDistance)(ref sd)).IsClassic)
			{
				return ZoneSystem.instance.ZonesWithinRadius(centre, candidate, ((SimulationDistance)(ref sd)).NearSimulationDistance, false);
			}
			return true;
		}

		internal static bool InDistant(Vector2s centre, Vector2s candidate, SimulationDistance sd)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			if (ZoneChebyshev(centre, candidate) > ((SimulationDistance)(ref sd)).TotalSimulationDistance)
			{
				return false;
			}
			if (!((SimulationDistance)(ref sd)).IsClassic)
			{
				return ZoneSystem.instance.ZonesWithinRadius(centre, candidate, ((SimulationDistance)(ref sd)).TotalSimulationDistance, true);
			}
			return true;
		}

		public static DirtyPeerState<ZDOID> StateFor(ZDOPeer peer)
		{
			return s_state.GetValue(peer, (ZDOPeer _) => new DirtyPeerState<ZDOID>());
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void DataRevisionSet(ZDO __instance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Mark(__instance.m_uid, isOwner: false);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void OwnerRevisionSet(ZDO __instance)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Mark(__instance.m_uid, isOwner: true);
		}

		private static void Mark(ZDOID id, bool isOwner)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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)
			if (id == ZDOID.None)
			{
				return;
			}
			if (isOwner)
			{
				MarksOwner++;
			}
			else
			{
				MarksData++;
				WatchdogMarks++;
			}
			if (!Active)
			{
				return;
			}
			ZDOMan instance = ZDOMan.instance;
			if (instance != null)
			{
				List<ZDOPeer> peers = instance.m_peers;
				for (int i = 0; i < peers.Count; i++)
				{
					StateFor(peers[i]).Pending.Add(id);
				}
			}
		}

		public static void ResetCounters()
		{
			MarksData = 0L;
			MarksOwner = 0L;
			FullScans = 0;
			DirtyRounds = 0;
			Deferred = 0;
			LastDrained = 0;
		}

		public static bool WatchdogShouldTrip(bool dirtySetsOn, bool disabled, bool recvSeen, long marksInWindow)
		{
			if (dirtySetsOn && !disabled && recvSeen)
			{
				return marksInWindow == 0;
			}
			return false;
		}

		public static bool WatchdogShouldRearm(bool disabled, long marksInWindow)
		{
			if (disabled)
			{
				return marksInWindow > 0;
			}
			return false;
		}

		[HarmonyPatch(typeof(ZDOMan), "CreateSyncList")]
		[HarmonyPrefix]
		private static bool CreateSyncListPrefix(ZDOMan __instance, ZDOPeer peer, List<ZDO> toSync, out bool __state)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: 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_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			__state = false;
			ZDOID lastId = ZDOID.None;
			ZDO lastZdo = null;
			bool active = Active;
			Vector3 refPos = peer.m_peer.GetRefPos();
			Vector2s zone = ZoneSystem.GetZone(refPos);
			DirtyPeerState<ZDOID> dirtyPeerState = StateFor(peer);
			bool flag = dirtyPeerState.NeedsFullScan((x: zone.x, y: zone.y), Time.time, Cfg.ReconcileSeconds.Value, active);
			if (!active)
			{
				return true;
			}
			if (flag)
			{
				FullScans++;
				__state = true;
				return true;
			}
			DirtyRounds++;
			SimulationDistance sd = peer.m_peer.m_simulationDistance;
			s_relayMinMs = Cfg.RelayMinIntervalMs.Value;
			s_ids.Clear();
			dirtyPeerState.Drain(s_ids, (ZDOID id) => Z(id) != null, delegate(ZDOID id)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				ZDO val = Z(id);
				Vector2s sector = val.GetSector();
				return InNear(zone, sector, sd) || (val.Distant && InDistant(zone, sector, sd));
			}, (ZDOID id) => peer.ShouldSend(Z(id)), (ZDOID id) => RelayThrottled(peer, Z(id)));
			LastDrained = s_ids.Count;
			int num = 0;
			for (int num2 = 0; num2 < s_ids.Count; num2++)
			{
				ZDO zDO = __instance.GetZDO(s_ids[num2]);
				if (InNear(zone, zDO.GetSector(), sd))
				{
					toSync.Add(zDO);
					num++;
				}
			}
			if (num < 10)
			{
				for (int num3 = 0; num3 < s_ids.Count; num3++)
				{
					ZDO zDO2 = __instance.GetZDO(s_ids[num3]);
					if (!InNear(zone, zDO2.GetSector(), sd))
					{
						toSync.Add(zDO2);
					}
				}
			}
			__instance.ServerSortSendZDOS(toSync, refPos, peer);
			__instance.AddForceSendZdos(peer, toSync);
			return false;
			ZDO Z(ZDOID id)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				if (id != lastId)
				{
					lastZdo = __instance.GetZDO(id);
					lastId = id;
				}
				return lastZdo;
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "CreateSyncList")]
		[HarmonyPostfix]
		private static void CreateSyncListPostfix(ZDOPeer peer, List<ZDO> toSync, bool __state)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (__state)
			{
				DirtyPeerState<ZDOID> dirtyPeerState = StateFor(peer);
				for (int i = 0; i < toSync.Count; i++)
				{
					dirtyPeerState.Pending.Add(toSync[i].m_uid);
				}
			}
		}

		private static bool RelayThrottled(ZDOPeer peer, ZDO z)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			int num = s_relayMinMs;
			if (num <= 0)
			{
				return false;
			}
			if ((int)z.Type == 1)
			{
				return false;
			}
			if (z.GetOwner() == peer.m_peer.m_uid)
			{
				return false;
			}
			if (!peer.m_zdos.TryGetValue(z.m_uid, out var value))
			{
				return false;
			}
			bool num2 = (Time.time - value.m_syncTime) * 1000f < (float)num;
			if (num2)
			{
				Deferred++;
			}
			return num2;
		}
	}
	[HarmonyPatch]
	public static class MeasurePatches
	{
		public static readonly RollingStats FrameMs = new RollingStats();

		public static readonly RollingStats SyncListMs = new RollingStats();

		public static readonly RollingStats SendMs = new RollingStats();

		public static readonly ChurnTally Recv = new ChurnTally();

		public static int MaxZ;

		public static int Rounds;

		private static readonly Stopwatch s_sync = new Stopwatch();

		private static readonly Stopwatch s_send = new Stopwatch();

		private static readonly Dictionary<ZDOID, (int count, int prefab, Vector3 pos)> s_hot = new Dictionary<ZDOID, (int, int, Vector3)>();

		[HarmonyPatch(typeof(ZDOMan), "CreateSyncList")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void SyncListPrefix()
		{
			s_sync.Restart();
		}

		[HarmonyPatch(typeof(ZDOMan), "CreateSyncList")]
		[HarmonyPostfix]
		private static void SyncListPostfix(ZDOMan __instance)
		{
			SyncListMs.Add(s_sync.Elapsed.TotalMilliseconds);
			int count = __instance.m_tempSectorObjects.Count;
			if (count > MaxZ)
			{
				MaxZ = count;
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void SendPrefix()
		{
			s_send.Restart();
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyPostfix]
		private static void SendPostfix()
		{
			SendMs.Add(s_send.Elapsed.TotalMilliseconds);
			Rounds++;
		}

		[HarmonyPatch(typeof(ZDO), "Deserialize")]
		[HarmonyPostfix]
		private static void DeserializePostfix(ZDO __instance)
		{
			//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)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Recv.Add(__instance.GetPrefab());
			DirtyPatches.WatchdogRecv++;
			ZDOID uid = __instance.m_uid;
			s_hot.TryGetValue(uid, out (int, int, Vector3) value);
			s_hot[uid] = (value.Item1 + 1, __instance.GetPrefab(), __instance.GetPosition());
		}

		public static List<KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)>> HotTop(int n)
		{
			return s_hot.OrderByDescending((KeyValuePair<ZDOID, (int count, int prefab, Vector3 pos)> x) => x.Value.count).Take(n).ToList();
		}

		public static void ResetAll()
		{
			FrameMs.Reset();
			SyncListMs.Reset();
			SendMs.Reset();
			MaxZ = 0;
			Rounds = 0;
			Recv.Reset();
			s_hot.Clear();
		}
	}
	[HarmonyPatch(typeof(ZRpc), "Update")]
	public static class ReceiveCapPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ZRpc __instance, float dt, ref ErrorCode __result)
		{
			int value = Cfg.MaxPacketsPerPeerPerFrame.Value;
			if (!Compat.ReplacementsAllowed || value <= 0)
			{
				return true;
			}
			if (!__instance.m_socket.IsConnected())
			{
				__result = (ErrorCode)1;
				return false;
			}
			for (int i = 0; i < value; i++)
			{
				ZPackage val = __instance.m_socket.Recv();
				if (val == null)
				{
					break;
				}
				__instance.m_recvPackages++;
				__instance.m_recvData += val.Size();
				try
				{
					__instance.HandlePackage(val);
				}
				catch (EndOfStreamException ex)
				{
					ZLog.LogError((object)("EndOfStreamException in ZRpc::HandlePackage: Assume incompatible version: " + ex.Message));
					__result = (ErrorCode)2;
					return false;
				}
				catch (Exception ex2)
				{
					ZLog.Log((object)("Exception in ZRpc::HandlePackage: " + ex2));
				}
			}
			__instance.UpdatePing(dt);
			__result = (ErrorCode)0;
			return false;
		}
	}
	[HarmonyPatch(typeof(Heightmap), "RebuildRenderMesh")]
	public static class RenderMeshPatch
	{
		public static long Skipped;

		[HarmonyPrefix]
		private static bool Prefix()
		{
			if (!Compat.ReplacementsAllowed || !Cfg.ServerSkipRenderMesh.Value)
			{
				return true;
			}
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsDedicated())
			{
				return true;
			}
			Skipped++;
			return false;
		}
	}
	[HarmonyPatch]
	public static class SortPatches
	{
		private static bool Active
		{
			get
			{
				if (Compat.ReplacementsAllowed && Cfg.TopKSort.Value && (Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "ServerSortSendZDOS")]
		[HarmonyPrefix]
		private static bool Prefix(List<ZDO> objects, Vector3 refPos, ZDOPeer peer)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return true;
			}
			float time = Time.time;
			long receiver = peer.m_peer.m_uid;
			Dictionary<ZDOID, PeerZDOInfo> zdos = peer.m_zdos;
			int k = ((Cfg.TopK.Value > 0) ? Cfg.TopK.Value : Math.Max(64, Cfg.SendWindowBytes.Value / 64));
			TopK.Select(objects, delegate(ZDO z)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_006d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				int num = (((int)z.Type != 1 || !z.HasOwner() || z.GetOwner() == receiver) ? (1 + (3 - z.Type)) : 0);
				float num2 = 100f;
				if (zdos.TryGetValue(z.m_uid, out var value))
				{
					num2 = Mathf.Clamp(time - value.m_syncTime, 0f, 100f);
				}
				double num3 = Vector3.Distance(z.GetPosition(), refPos) - num2 * 1.5f;
				return (double)num * 100000.0 + num3;
			}, k);
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}