Decompiled source of SkadiNet v1.0.0

SkadiNet.dll

Decompiled a day 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.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SkadiNet")]
[assembly: AssemblyTitle("SkadiNet")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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 SkadiNet
{
	internal enum ClientCleanupKind
	{
		GarbageCollect,
		UnloadUnusedAssets
	}
	internal enum ClientCriticalWindow
	{
		InitialSync,
		Teleport,
		FullSnapshotBurst,
		Combat,
		ShipTravel
	}
	internal struct MemoryPressureSnapshot
	{
		public bool Known;

		public ulong TotalMB;

		public ulong AvailableMB;

		public int LoadPercent;

		public override string ToString()
		{
			if (!Known)
			{
				return "unknown";
			}
			return $"load={LoadPercent}% available={AvailableMB}MB total={TotalMB}MB";
		}
	}
	internal static class ClientStutterGuard
	{
		[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
		private struct MEMORYSTATUSEX
		{
			public uint dwLength;

			public uint dwMemoryLoad;

			public ulong ullTotalPhys;

			public ulong ullAvailPhys;

			public ulong ullTotalPageFile;

			public ulong ullAvailPageFile;

			public ulong ullTotalVirtual;

			public ulong ullAvailVirtual;

			public ulong ullAvailExtendedVirtual;
		}

		[CompilerGenerated]
		private sealed class <CleanupScheduler>d__42 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CleanupScheduler>d__42(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Expected O, but got Unknown
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					if (IsActive && RunCleanupWhenIdle)
					{
						TryRunPendingCleanup(forced: false);
					}
				}
				else
				{
					<>1__state = -1;
				}
				float num2 = Math.Max(0.25f, EffectiveConfig.ClientStutterIdleCleanupPollSeconds);
				<>2__current = (object)new WaitForSeconds(num2);
				<>1__state = 1;
				return true;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static readonly Dictionary<ClientCriticalWindow, double> CriticalUntil = new Dictionary<ClientCriticalWindow, double>();

		private static Plugin _plugin;

		private static Coroutine _cleanupCoroutine;

		private static bool _pendingGc;

		private static bool _pendingUnload;

		private static double _firstPendingSince;

		private static bool _runningCleanup;

		private static AsyncOperation _lastUnloadOperation;

		private static bool DelayGcCollect => true;

		private static bool DelayUnusedAssetCleanup => false;

		private static bool DelayDuringInitialSync => true;

		private static bool DelayDuringTeleport => true;

		private static bool DelayDuringFullSnapshotBurst => true;

		private static bool DelayDuringCombat => true;

		private static bool DelayDuringShipTravel => true;

		private static bool RunCleanupWhenIdle => true;

		private static bool UseMemoryPressureGate => true;

		internal static bool IsActive
		{
			get
			{
				if (!EffectiveConfig.ClientStutterGuardEnabled)
				{
					return false;
				}
				if (IsDedicatedLike())
				{
					return false;
				}
				return true;
			}
		}

		internal static void Initialize(Plugin plugin)
		{
			_plugin = plugin;
			CriticalUntil.Clear();
			_pendingGc = false;
			_pendingUnload = false;
			_firstPendingSince = 0.0;
			_runningCleanup = false;
			_lastUnloadOperation = null;
			EnsureCleanupScheduler();
		}

		internal static void Shutdown()
		{
			try
			{
				if (_cleanupCoroutine != null && (Object)(object)_plugin != (Object)null)
				{
					((MonoBehaviour)_plugin).StopCoroutine(_cleanupCoroutine);
				}
			}
			catch
			{
			}
			_cleanupCoroutine = null;
			_plugin = null;
		}

		private static bool IsDedicatedLike()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			try
			{
				if (NetReflection.IsDedicatedServer())
				{
					return true;
				}
			}
			catch
			{
			}
			try
			{
				if (Application.isBatchMode)
				{
					return true;
				}
				if ((int)SystemInfo.graphicsDeviceType == 4)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		internal static void MarkCriticalWindow(ClientCriticalWindow window, float seconds)
		{
			if (IsActive && !(seconds <= 0f) && IsWindowEnabled(window))
			{
				double num = Time.realtimeSinceStartupAsDouble + (double)seconds;
				if (!CriticalUntil.TryGetValue(window, out var value) || value < num)
				{
					CriticalUntil[window] = num;
				}
			}
		}

		internal static void MarkInitialSyncWindow()
		{
			MarkCriticalWindow(ClientCriticalWindow.InitialSync, Math.Max(0.1f, EffectiveConfig.ClientStutterInitialSyncWindowSeconds));
		}

		internal static void MarkTeleportWindow()
		{
			MarkCriticalWindow(ClientCriticalWindow.Teleport, Math.Max(0.1f, EffectiveConfig.ClientStutterTeleportWindowSeconds));
		}

		internal static void MarkFullSnapshotBurstWindow()
		{
			MarkCriticalWindow(ClientCriticalWindow.FullSnapshotBurst, Math.Max(0.1f, EffectiveConfig.ClientStutterFullSnapshotWindowSeconds));
		}

		internal static void MarkCombatWindow()
		{
			MarkCriticalWindow(ClientCriticalWindow.Combat, Math.Max(0.1f, EffectiveConfig.ClientStutterCombatWindowSeconds));
		}

		internal static void MarkShipTravelWindow()
		{
			MarkCriticalWindow(ClientCriticalWindow.ShipTravel, Math.Max(0.1f, EffectiveConfig.ClientStutterShipWindowSeconds));
		}

		internal static bool TryHandleGcCollect()
		{
			if (!IsActive || !DelayGcCollect || _runningCleanup)
			{
				return true;
			}
			if (!RunCleanupWhenIdle)
			{
				return true;
			}
			if (ShouldDelayCleanup(ClientCleanupKind.GarbageCollect, out var reason))
			{
				RequestPending(ClientCleanupKind.GarbageCollect, reason);
				return false;
			}
			return true;
		}

		internal static bool TryHandleUnloadUnusedAssetsPrefix(ref AsyncOperation result)
		{
			if (!IsActive || !DelayUnusedAssetCleanup || _runningCleanup)
			{
				return true;
			}
			if (!RunCleanupWhenIdle)
			{
				return true;
			}
			if (ShouldDelayCleanup(ClientCleanupKind.UnloadUnusedAssets, out var reason))
			{
				RequestPending(ClientCleanupKind.UnloadUnusedAssets, reason);
				if (_lastUnloadOperation != null)
				{
					result = _lastUnloadOperation;
					return false;
				}
				return true;
			}
			return true;
		}

		internal static void OnUnloadUnusedAssetsPostfix(AsyncOperation result)
		{
			if (result != null)
			{
				_lastUnloadOperation = result;
			}
		}

		private static void RequestPending(ClientCleanupKind kind, string reason)
		{
			if (kind == ClientCleanupKind.GarbageCollect)
			{
				_pendingGc = true;
			}
			if (kind == ClientCleanupKind.UnloadUnusedAssets)
			{
				_pendingUnload = true;
			}
			if (_firstPendingSince <= 0.0)
			{
				_firstPendingSince = Time.realtimeSinceStartupAsDouble;
			}
			EnsureCleanupScheduler();
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"ClientStutterGuard: deferred {kind} ({reason}).");
			}
		}

		private static void EnsureCleanupScheduler()
		{
			if (_cleanupCoroutine == null && !((Object)(object)_plugin == (Object)null) && IsActive && RunCleanupWhenIdle)
			{
				_cleanupCoroutine = ((MonoBehaviour)_plugin).StartCoroutine(CleanupScheduler());
			}
		}

		[IteratorStateMachine(typeof(<CleanupScheduler>d__42))]
		private static IEnumerator CleanupScheduler()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CleanupScheduler>d__42(0);
		}

		private static bool TryRunPendingCleanup(bool forced)
		{
			if (!_pendingGc && !_pendingUnload)
			{
				return false;
			}
			if (!IsActive && !forced)
			{
				return false;
			}
			if (_runningCleanup)
			{
				return false;
			}
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			bool flag = _firstPendingSince > 0.0 && realtimeSinceStartupAsDouble - _firstPendingSince >= (double)Math.Max(1f, EffectiveConfig.ClientStutterMaxDelaySeconds);
			if (!forced && !flag && TryGetCriticalReason(out var reason))
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)("ClientStutterGuard: pending cleanup waits for " + reason + "."));
				}
				return false;
			}
			if (!forced && !flag && UseMemoryPressureGate && IsMemoryPlentiful(out var snapshot))
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"ClientStutterGuard: pending cleanup waits; memory plentiful ({snapshot}).");
				}
				return false;
			}
			bool flag2 = _pendingUnload && DelayUnusedAssetCleanup;
			bool flag3 = _pendingGc && DelayGcCollect;
			_pendingUnload = false;
			_pendingGc = false;
			_firstPendingSince = 0.0;
			_runningCleanup = true;
			try
			{
				if (flag2)
				{
					if (ModConfig.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)"ClientStutterGuard: running coalesced Resources.UnloadUnusedAssets.");
					}
					AsyncOperation val = Resources.UnloadUnusedAssets();
					if (val != null)
					{
						_lastUnloadOperation = val;
					}
				}
				if (flag3)
				{
					if (ModConfig.DebugLogging.Value)
					{
						Plugin.Log.LogDebug((object)"ClientStutterGuard: running coalesced GC.Collect.");
					}
					GC.Collect();
				}
			}
			finally
			{
				_runningCleanup = false;
			}
			return flag2 || flag3;
		}

		private static bool ShouldDelayCleanup(ClientCleanupKind kind, out string reason)
		{
			reason = "safe window";
			if (!IsActive)
			{
				return false;
			}
			if (_firstPendingSince > 0.0 && Time.realtimeSinceStartupAsDouble - _firstPendingSince >= (double)Math.Max(1f, EffectiveConfig.ClientStutterMaxDelaySeconds))
			{
				reason = "max delay exceeded";
				return false;
			}
			if (TryGetCriticalReason(out var reason2))
			{
				if (UseMemoryPressureGate && IsMemoryPressure(out var snapshot))
				{
					reason = $"memory pressure overrides critical window ({snapshot})";
					return false;
				}
				reason = reason2;
				return true;
			}
			if (UseMemoryPressureGate && IsMemoryPlentiful(out var snapshot2))
			{
				reason = $"memory plentiful ({snapshot2})";
				return true;
			}
			return false;
		}

		private static bool TryGetCriticalReason(out string reason)
		{
			reason = null;
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			double num = 0.0;
			ClientCriticalWindow clientCriticalWindow = ClientCriticalWindow.InitialSync;
			foreach (KeyValuePair<ClientCriticalWindow, double> item in CriticalUntil)
			{
				if (item.Value > realtimeSinceStartupAsDouble && item.Value > num && IsWindowEnabled(item.Key))
				{
					clientCriticalWindow = item.Key;
					num = item.Value;
				}
			}
			if (num <= realtimeSinceStartupAsDouble)
			{
				return false;
			}
			reason = $"{clientCriticalWindow} for {num - realtimeSinceStartupAsDouble:F1}s";
			return true;
		}

		private static bool IsWindowEnabled(ClientCriticalWindow window)
		{
			return window switch
			{
				ClientCriticalWindow.InitialSync => DelayDuringInitialSync, 
				ClientCriticalWindow.Teleport => DelayDuringTeleport, 
				ClientCriticalWindow.FullSnapshotBurst => DelayDuringFullSnapshotBurst, 
				ClientCriticalWindow.Combat => DelayDuringCombat, 
				ClientCriticalWindow.ShipTravel => DelayDuringShipTravel, 
				_ => true, 
			};
		}

		private static bool IsMemoryPlentiful(out MemoryPressureSnapshot snapshot)
		{
			snapshot = GetMemorySnapshot();
			if (!snapshot.Known)
			{
				return false;
			}
			if (snapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent))
			{
				return snapshot.AvailableMB >= (ulong)Math.Max(0, EffectiveConfig.ClientStutterMinimumFreeMemoryMB);
			}
			return false;
		}

		private static bool IsMemoryPressure(out MemoryPressureSnapshot snapshot)
		{
			snapshot = GetMemorySnapshot();
			if (!snapshot.Known)
			{
				return false;
			}
			if (snapshot.LoadPercent < Math.Max(1, EffectiveConfig.ClientStutterMemoryPressureThresholdPercent))
			{
				return snapshot.AvailableMB < (ulong)Math.Max(0, EffectiveConfig.ClientStutterMinimumFreeMemoryMB);
			}
			return true;
		}

		private static MemoryPressureSnapshot GetMemorySnapshot()
		{
			if (TryGetWindowsMemory(out var snapshot))
			{
				return snapshot;
			}
			if (TryGetProcMemInfo(out var snapshot2))
			{
				return snapshot2;
			}
			try
			{
				if (SystemInfo.systemMemorySize > 0)
				{
					ulong num = (ulong)SystemInfo.systemMemorySize;
					MemoryPressureSnapshot result = default(MemoryPressureSnapshot);
					result.Known = true;
					result.TotalMB = num;
					result.AvailableMB = num;
					result.LoadPercent = 0;
					return result;
				}
			}
			catch
			{
			}
			MemoryPressureSnapshot result2 = default(MemoryPressureSnapshot);
			result2.Known = false;
			return result2;
		}

		private static bool TryGetWindowsMemory(out MemoryPressureSnapshot snapshot)
		{
			snapshot = default(MemoryPressureSnapshot);
			try
			{
				MEMORYSTATUSEX lpBuffer = default(MEMORYSTATUSEX);
				lpBuffer.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
				if (!GlobalMemoryStatusEx(ref lpBuffer))
				{
					return false;
				}
				snapshot.Known = true;
				snapshot.TotalMB = lpBuffer.ullTotalPhys / 1024 / 1024;
				snapshot.AvailableMB = lpBuffer.ullAvailPhys / 1024 / 1024;
				snapshot.LoadPercent = (int)lpBuffer.dwMemoryLoad;
				return snapshot.TotalMB != 0;
			}
			catch
			{
				return false;
			}
		}

		private static bool TryGetProcMemInfo(out MemoryPressureSnapshot snapshot)
		{
			snapshot = default(MemoryPressureSnapshot);
			try
			{
				if (!File.Exists("/proc/meminfo"))
				{
					return false;
				}
				ulong num = 0uL;
				ulong num2 = 0uL;
				string[] array = File.ReadAllLines("/proc/meminfo");
				foreach (string text in array)
				{
					if (text.StartsWith("MemTotal:", StringComparison.Ordinal))
					{
						num = ParseKb(text);
					}
					else if (text.StartsWith("MemAvailable:", StringComparison.Ordinal))
					{
						num2 = ParseKb(text);
					}
				}
				if (num == 0L || num2 == 0L)
				{
					return false;
				}
				snapshot.Known = true;
				snapshot.TotalMB = num / 1024;
				snapshot.AvailableMB = num2 / 1024;
				snapshot.LoadPercent = (int)Math.Round(100.0 * (1.0 - (double)num2 / (double)num));
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static ulong ParseKb(string line)
		{
			string[] array = line.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 1; i < array.Length; i++)
			{
				if (ulong.TryParse(array[i], out var result))
				{
					return result;
				}
			}
			return 0uL;
		}

		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardGcCollectPatch
	{
		[CompilerGenerated]
		private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private MethodBase <>2__current;

			private int <>l__initialThreadId;

			private MethodInfo[] <>7__wrap1;

			private int <>7__wrap2;

			MethodBase IEnumerator<MethodBase>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <TargetMethods>d__0(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_006e;
				}
				<>1__state = -1;
				<>7__wrap1 = typeof(GC).GetMethods(BindingFlags.Static | BindingFlags.Public);
				<>7__wrap2 = 0;
				goto IL_007c;
				IL_006e:
				<>7__wrap2++;
				goto IL_007c;
				IL_007c:
				if (<>7__wrap2 < <>7__wrap1.Length)
				{
					MethodInfo methodInfo = <>7__wrap1[<>7__wrap2];
					if (methodInfo.Name == "Collect")
					{
						<>2__current = methodInfo;
						<>1__state = 1;
						return true;
					}
					goto IL_006e;
				}
				<>7__wrap1 = null;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <TargetMethods>d__0(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<MethodBase>)this).GetEnumerator();
			}
		}

		[IteratorStateMachine(typeof(<TargetMethods>d__0))]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <TargetMethods>d__0(-2);
		}

		private static bool Prefix()
		{
			return ClientStutterGuard.TryHandleGcCollect();
		}
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardUnloadUnusedAssetsPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Resources), "UnloadUnusedAssets", (Type[])null, (Type[])null);
		}

		private static bool Prefix(ref AsyncOperation __result)
		{
			return ClientStutterGuard.TryHandleUnloadUnusedAssetsPrefix(ref __result);
		}

		private static void Postfix(AsyncOperation __result)
		{
			ClientStutterGuard.OnUnloadUnusedAssetsPostfix(__result);
		}
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardZNetConnectionPatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = ReflectionCache.ZNetType ?? AccessTools.TypeByName("ZNet");
			Type type2 = ReflectionCache.ZNetPeerType ?? AccessTools.TypeByName("ZNetPeer");
			return AccessTools.Method(type, "OnNewConnection", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method(type, "OnNewConnection", (Type[])null, (Type[])null);
		}

		private static void Postfix()
		{
			ClientStutterGuard.MarkInitialSyncWindow();
		}
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardZdoDataPatch
	{
		[CompilerGenerated]
		private sealed class <TargetMethods>d__0 : IEnumerable<MethodBase>, IEnumerable, IEnumerator<MethodBase>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private MethodBase <>2__current;

			private int <>l__initialThreadId;

			private MethodInfo[] <>7__wrap1;

			private int <>7__wrap2;

			MethodBase IEnumerator<MethodBase>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <TargetMethods>d__0(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				int num = <>1__state;
				if (num != 0)
				{
					if (num != 1)
					{
						return false;
					}
					<>1__state = -1;
					goto IL_0084;
				}
				<>1__state = -1;
				Type type = ReflectionCache.ZDOManType ?? AccessTools.TypeByName("ZDOMan");
				if (type == null)
				{
					return false;
				}
				<>7__wrap1 = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				<>7__wrap2 = 0;
				goto IL_0092;
				IL_0092:
				if (<>7__wrap2 < <>7__wrap1.Length)
				{
					MethodInfo methodInfo = <>7__wrap1[<>7__wrap2];
					if (methodInfo.Name == "RPC_ZDOData")
					{
						<>2__current = methodInfo;
						<>1__state = 1;
						return true;
					}
					goto IL_0084;
				}
				<>7__wrap1 = null;
				return false;
				IL_0084:
				<>7__wrap2++;
				goto IL_0092;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<MethodBase> IEnumerable<MethodBase>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <TargetMethods>d__0(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<MethodBase>)this).GetEnumerator();
			}
		}

		[IteratorStateMachine(typeof(<TargetMethods>d__0))]
		private static IEnumerable<MethodBase> TargetMethods()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <TargetMethods>d__0(-2);
		}

		private static void Prefix(object[] __args)
		{
			try
			{
				if (NetReflection.IsServer() || __args == null || ReflectionCache.ZPackageType == null)
				{
					return;
				}
				foreach (object obj in __args)
				{
					if (obj != null && ReflectionCache.ZPackageType.IsInstanceOfType(obj) && ZPackageTools.Size(obj) >= 32768)
					{
						ClientStutterGuard.MarkFullSnapshotBurstWindow();
						break;
					}
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardLoadingScreenPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(AccessTools.TypeByName("ZNetScene"), "InLoadingScreen", (Type[])null, (Type[])null);
		}

		private static void Postfix(bool __result)
		{
			if (__result)
			{
				ClientStutterGuard.MarkTeleportWindow();
			}
		}
	}
	[HarmonyPatch]
	internal static class ClientStutterGuardShipTravelPatch
	{
		private static FieldInfo _bodyField;

		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("Ship");
			_bodyField = ReflectionCache.SilentField(type, "m_body");
			return AccessTools.Method(type, "CustomFixedUpdate", (Type[])null, (Type[])null) ?? AccessTools.Method(type, "FixedUpdate", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __instance)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			if (__instance == null)
			{
				return;
			}
			try
			{
				object? obj = _bodyField?.GetValue(__instance);
				Rigidbody val = (Rigidbody)((obj is Rigidbody) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					Vector3 linearVelocity = val.linearVelocity;
					if (((Vector3)(ref linearVelocity)).sqrMagnitude > 4f)
					{
						ClientStutterGuard.MarkShipTravelWindow();
					}
				}
			}
			catch
			{
			}
		}
	}
	[HarmonyPatch]
	internal static class MonsterAISetTargetOwnershipPatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("MonsterAI");
			Type type2 = AccessTools.TypeByName("Character");
			return AccessTools.Method(type, "SetTarget", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method("MonsterAI:SetTarget", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __instance, object[] __args)
		{
			if (__args != null && __args.Length != 0)
			{
				ClientStutterGuard.MarkCombatWindow();
				OwnershipManager.TryTransferCombatOwnership(__instance, __args[0]);
			}
		}
	}
	internal static class CompressionDiagnostics
	{
		private const double SummaryIntervalSeconds = 10.0;

		private static int _encoded;

		private static int _decoded;

		private static long _rawEncodeBytes;

		private static long _compressedEncodeBytes;

		private static long _compressedDecodeBytes;

		private static long _rawDecodeBytes;

		private static double _encodeSeconds;

		private static double _decodeSeconds;

		private static double _maxEncodeSeconds;

		private static double _maxDecodeSeconds;

		private static double _nextSummaryTime;

		internal static void RecordEncode(int rawBytes, int compressedBytes, double seconds)
		{
			if (ModConfig.DebugLogging.Value)
			{
				_encoded++;
				_rawEncodeBytes += Math.Max(0, rawBytes);
				_compressedEncodeBytes += Math.Max(0, compressedBytes);
				_encodeSeconds += Math.Max(0.0, seconds);
				if (seconds > _maxEncodeSeconds)
				{
					_maxEncodeSeconds = seconds;
				}
				LogIfDue();
			}
		}

		internal static void RecordDecode(int compressedBytes, int rawBytes, double seconds)
		{
			if (ModConfig.DebugLogging.Value)
			{
				_decoded++;
				_compressedDecodeBytes += Math.Max(0, compressedBytes);
				_rawDecodeBytes += Math.Max(0, rawBytes);
				_decodeSeconds += Math.Max(0.0, seconds);
				if (seconds > _maxDecodeSeconds)
				{
					_maxDecodeSeconds = seconds;
				}
				LogIfDue();
			}
		}

		private static void LogIfDue()
		{
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			if (!(realtimeSinceStartupAsDouble < _nextSummaryTime))
			{
				_nextSummaryTime = realtimeSinceStartupAsDouble + 10.0;
				if (_encoded > 0 || _decoded > 0)
				{
					float num = ((_rawEncodeBytes > 0) ? ((float)_compressedEncodeBytes / (float)_rawEncodeBytes) : 0f);
					Plugin.Log.LogDebug((object)($"Compression summary {10.0:F0}s: encoded={_encoded} raw={FormatBytes(_rawEncodeBytes)} compressed={FormatBytes(_compressedEncodeBytes)} ratio={num:F2} " + $"encodeAvgMs={_encodeSeconds / (double)Math.Max(1, _encoded) * 1000.0:F2} encodeMaxMs={_maxEncodeSeconds * 1000.0:F2} " + $"decoded={_decoded} compressedIn={FormatBytes(_compressedDecodeBytes)} rawOut={FormatBytes(_rawDecodeBytes)} " + $"decodeAvgMs={_decodeSeconds / (double)Math.Max(1, _decoded) * 1000.0:F2} decodeMaxMs={_maxDecodeSeconds * 1000.0:F2}"));
				}
				_encoded = 0;
				_decoded = 0;
				_rawEncodeBytes = 0L;
				_compressedEncodeBytes = 0L;
				_compressedDecodeBytes = 0L;
				_rawDecodeBytes = 0L;
				_encodeSeconds = 0.0;
				_decodeSeconds = 0.0;
				_maxEncodeSeconds = 0.0;
				_maxDecodeSeconds = 0.0;
			}
		}

		private static string FormatBytes(long bytes)
		{
			if (bytes >= 1048576)
			{
				return $"{(float)bytes / 1048576f:F1}MB";
			}
			if (bytes >= 1024)
			{
				return $"{(float)bytes / 1024f:F1}KB";
			}
			return $"{bytes}B";
		}
	}
	[HarmonyPatch]
	internal static class ZSteamSocketSendCompressionPatch
	{
		private static MethodBase TargetMethod()
		{
			return ReflectionCache.ZSteamSocketSendMethod ?? AccessTools.Method("ZSteamSocket:Send", (Type[])null, (Type[])null);
		}

		private static void Prefix(object __instance, ref object __0)
		{
			if (!EffectiveConfig.CompressionEnabled || __instance == null || __0 == null || !FeatureNegotiation.IsCompressionActiveForSocket(__instance))
			{
				return;
			}
			try
			{
				int rawBytes = ZPackageTools.Size(__0);
				double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
				if (ZPackageTools.TryBuildCompressedPackage(__0, out var compressedPackage))
				{
					__0 = compressedPackage;
					CompressionDiagnostics.RecordEncode(rawBytes, ZPackageTools.Size(compressedPackage), Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble);
				}
			}
			catch (Exception ex)
			{
				FeatureNegotiation.RecordCompressionFailure(__instance);
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("ZSteamSocket.Send compression failed: " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ZSteamSocketRecvCompressionPatch
	{
		private static MethodBase TargetMethod()
		{
			return ReflectionCache.ZSteamSocketRecvMethod ?? AccessTools.Method("ZSteamSocket:Recv", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __instance, ref object __result)
		{
			if (__result == null)
			{
				return;
			}
			try
			{
				int compressedBytes = ZPackageTools.Size(__result);
				double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
				if (ZPackageTools.TryDecompressPackage(__result, out var rawPackage))
				{
					__result = rawPackage;
					CompressionDiagnostics.RecordDecode(compressedBytes, ZPackageTools.Size(rawPackage), Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble);
				}
			}
			catch (Exception ex)
			{
				FeatureNegotiation.RecordCompressionFailure(__instance);
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("ZSteamSocket.Recv decompression failed: " + ex.Message));
				}
			}
		}
	}
	internal enum ConfigSyncScope
	{
		ServerSynced,
		ClientLocal,
		MigrationOnly
	}
	internal static class ConfigSyncManager
	{
		private static MethodInfo _addConfigEntryMethod;

		internal static ConfigSync Sync { get; private set; }

		internal static ConfigEntry<bool> LockServerConfig { get; private set; }

		internal static void Initialize(ConfigFile config)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			Sync = new ConfigSync("sighsorry.SkadiNet")
			{
				DisplayName = "SkadiNet",
				CurrentVersion = "1.0.0",
				MinimumRequiredVersion = "1.0.0",
				ModRequired = true
			};
			LockServerConfig = config.Bind<bool>("General", "LockServerConfig", true, new ConfigDescription("Lock server-synced config for non-admin clients.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = 90
				}
			}));
			Sync.AddLockingConfigEntry<bool>(LockServerConfig).SynchronizedConfig = true;
		}

		internal static ConfigEntry<T> Bind<T>(ConfigFile config, string group, string name, T value, string description, ConfigSyncScope scope)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			return Bind(config, group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), scope);
		}

		internal static ConfigEntry<T> Bind<T>(ConfigFile config, string group, string name, T value, ConfigDescription description, ConfigSyncScope scope)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			string text = SuffixFor(scope);
			ConfigDescription val = new ConfigDescription(description.Description + text, description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = config.Bind<T>(group, name, value, val);
			if (scope != ConfigSyncScope.MigrationOnly)
			{
				Register((ConfigEntryBase)(object)val2, scope == ConfigSyncScope.ServerSynced);
			}
			return val2;
		}

		private static string SuffixFor(ConfigSyncScope scope)
		{
			return scope switch
			{
				ConfigSyncScope.ServerSynced => " [Synced with Server]", 
				ConfigSyncScope.ClientLocal => " [Client Local; Not Synced with Server]", 
				ConfigSyncScope.MigrationOnly => " [Migration Only; Not Synced with Server]", 
				_ => throw new ArgumentOutOfRangeException("scope", scope, null), 
			};
		}

		private static void Register(ConfigEntryBase entry, bool synchronized)
		{
			if (Sync != null && entry != null && (LockServerConfig == null || !object.Equals(entry.Definition, ((ConfigEntryBase)LockServerConfig).Definition)) && GetAddConfigEntryMethod().MakeGenericMethod(entry.SettingType).Invoke(Sync, new object[1] { entry }) is OwnConfigEntryBase ownConfigEntryBase)
			{
				ownConfigEntryBase.SynchronizedConfig = synchronized;
			}
		}

		private static MethodInfo GetAddConfigEntryMethod()
		{
			if (_addConfigEntryMethod != null)
			{
				return _addConfigEntryMethod;
			}
			_addConfigEntryMethod = typeof(ConfigSync).GetMethods(BindingFlags.Instance | BindingFlags.Public).First((MethodInfo method) => method.Name == "AddConfigEntry" && method.IsGenericMethodDefinition);
			return _addConfigEntryMethod;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public int? Order;
	}
	internal static class EffectiveConfig
	{
		internal const int SteamSendRateBytes = 36000000;

		internal static bool SchedulerEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.SchedulerThroughput);
				}
				return false;
			}
		}

		internal static bool PayloadReducerEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.PayloadReducerStrength);
				}
				return false;
			}
		}

		internal static bool CompressionEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.CompressionAggression);
				}
				return false;
			}
		}

		internal static bool RpcAoiEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.RpcAoiAggression);
				}
				return false;
			}
		}

		internal static bool ClientStutterGuardEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.ClientStutterGuardStrength);
				}
				return false;
			}
		}

		internal static bool AdaptiveOwnershipEnabled
		{
			get
			{
				if (ModConfig.Enabled.Value)
				{
					return IsPositive(ModConfig.OwnershipIntensity);
				}
				return false;
			}
		}

		internal static bool PeerQualityEnabled => AdaptiveOwnershipEnabled;

		internal static bool OwnerHintsEnabled => AdaptiveOwnershipEnabled;

		internal static float SendInterval => Map(ModConfig.SchedulerThroughput, 0.1f, 0.05f, 0.02f);

		internal static float MinSendInterval => Map(ModConfig.SchedulerThroughput, 0.06f, 0.03f, 0.015f);

		internal static float MaxSendInterval => Map(ModConfig.SchedulerThroughput, 0.2f, 0.1f, 0.045f);

		internal static int BasePeersPerTick => MapInt(ModConfig.SchedulerThroughput, 1, 4, 12);

		internal static int MaxPeersPerTick => MapInt(ModConfig.SchedulerThroughput, 4, 12, 32);

		internal static int ZdoQueueLimitBytes => MapInt(ModConfig.SchedulerThroughput, 10240, 65536, 196608);

		internal static int ZdoQueueMinPackageBytes => MapInt(ModConfig.SchedulerThroughput, 2048, 2048, 768);

		internal static int PeerQueueSoftLimitBytes => MapInt(ModConfig.SchedulerThroughput, 1572864, 524288, 196608);

		internal static int PeerQueueHardLimitBytes => MapInt(ModConfig.SchedulerThroughput, 6291456, 2097152, 786432);

		internal static float LaggingPeerMaxSkipSeconds => Map(ModConfig.SchedulerThroughput, 3f, 1f, 0.2f);

		internal static float PayloadVec3CullSize => Map(ModConfig.PayloadReducerStrength, 0.005f, 0.04f, 0.12f);

		internal static float PayloadQuaternionDotThreshold => Map(ModConfig.PayloadReducerStrength, 0.9998f, 0.995f, 0.985f);

		internal static float PayloadForceRefreshSeconds => Map(ModConfig.PayloadReducerStrength, 0.15f, 1f, 3f);

		internal static int CompressionThresholdBytes => MapInt(ModConfig.CompressionAggression, 8192, 1024, 128);

		internal static float CompressionMinUsefulRatio => Map(ModConfig.CompressionAggression, 0.7f, 0.9f, 0.99f);

		internal static int CompressionDisableAfterFailures => 1;

		internal static float RpcAoiVisualRadius => Map(ModConfig.RpcAoiAggression, 640f, 192f, 64f);

		internal static float ClientStutterInitialSyncWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 2f, 10f, 24f);

		internal static float ClientStutterTeleportWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 1f, 5f, 14f);

		internal static float ClientStutterFullSnapshotWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.25f, 1.5f, 5f);

		internal static float ClientStutterCombatWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.35f, 2f, 6f);

		internal static float ClientStutterShipWindowSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.35f, 2f, 6f);

		internal static float ClientStutterMaxDelaySeconds => Map(ModConfig.ClientStutterGuardStrength, 6f, 30f, 90f);

		internal static int ClientStutterMemoryPressureThresholdPercent => MapInt(ModConfig.ClientStutterGuardStrength, 55, 75, 95);

		internal static int ClientStutterMinimumFreeMemoryMB => MapInt(ModConfig.ClientStutterGuardStrength, 6144, 2048, 512);

		internal static float ClientStutterIdleCleanupPollSeconds => Map(ModConfig.ClientStutterGuardStrength, 0.25f, 1f, 4f);

		internal static float PeerPingEmaHalfLifeSeconds => Map(ModConfig.OwnershipIntensity, 6f, 2.5f, 0.75f);

		internal static int PeerPingSampleWindow => MapInt(ModConfig.OwnershipIntensity, 120, 60, 20);

		internal static float PeerQualityMeanWeight => Map(ModConfig.OwnershipIntensity, 0.35f, 0f, 0f);

		internal static float PeerQualityStdDevWeight => Map(ModConfig.OwnershipIntensity, 0.1f, 0.25f, 0.7f);

		internal static float PeerQualityJitterWeight => Map(ModConfig.OwnershipIntensity, 0.2f, 0.5f, 1.2f);

		internal static float PeerQualityEmaWeight => 1f;

		internal static float MaxCandidatePingMs => Map(ModConfig.OwnershipIntensity, 320f, 220f, 140f);

		internal static float MaxCandidateJitterMs => Map(ModConfig.OwnershipIntensity, 180f, 100f, 45f);

		internal static int OwnershipScanBudget => MapInt(ModConfig.OwnershipIntensity, 24, 96, 256);

		internal static int OwnershipScanStride => MapInt(ModConfig.OwnershipIntensity, 10, 4, 1);

		internal static float OwnershipScanIntervalSeconds => Map(ModConfig.OwnershipIntensity, 3f, 1f, 0.25f);

		internal static float OwnershipRelativeHysteresis => Map(ModConfig.OwnershipIntensity, 0.03f, 0.15f, 0.4f);

		internal static float OwnershipAbsoluteHysteresisMs => Map(ModConfig.OwnershipIntensity, 5f, 20f, 90f);

		internal static float OwnerSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 0.75f, 3f, 12f);

		internal static float OwnerHintSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 1f, 5f, 16f);

		internal static float ShipOwnerSwitchCooldownSeconds => Map(ModConfig.OwnershipIntensity, 3f, 8f, 24f);

		internal static float RecoverUnownedAfterSeconds => Map(ModConfig.OwnershipIntensity, 0.75f, 2f, 6f);

		internal static float OwnershipCandidateRadius => Map(ModConfig.OwnershipIntensity, 80f, 160f, 360f);

		internal static float OwnerHintCandidateRadius => Map(ModConfig.OwnershipIntensity, 128f, 256f, 640f);

		internal static float OwnershipDistanceScoreWeight => Map(ModConfig.OwnershipIntensity, 0.4f, 0.2f, 0.06f);

		internal static float OwnershipLoadPenaltyPerZdo => Map(ModConfig.OwnershipIntensity, 0.5f, 0.35f, 0.15f);

		internal static float ServerFallbackPenaltyMs => Map(ModConfig.OwnershipIntensity, 450f, 650f, 1000f);

		internal static float OwnerHintScoreBonusMs => Map(ModConfig.OwnershipIntensity, 20f, 90f, 240f);

		internal static float OwnerHintLifetimeSeconds => Map(ModConfig.OwnershipIntensity, 2f, 8f, 20f);

		private static bool IsPositive(ConfigEntry<int> entry)
		{
			return Clamp(entry?.Value ?? 0, 0, 100) > 0;
		}

		private static float Strength(ConfigEntry<int> entry)
		{
			return (float)Clamp(entry?.Value ?? 50, 0, 100) / 100f;
		}

		private static float Map(ConfigEntry<int> entry, float safe, float current, float aggressive)
		{
			float num = Strength(entry);
			if (!(num <= 0.5f))
			{
				return Lerp(current, aggressive, (num - 0.5f) * 2f);
			}
			return Lerp(safe, current, num * 2f);
		}

		private static int MapInt(ConfigEntry<int> entry, int safe, int current, int aggressive)
		{
			return (int)Math.Round(Map(entry, safe, current, aggressive));
		}

		private static float Clamp(float value, float min, float max)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return min;
			}
			return Math.Max(min, Math.Min(max, value));
		}

		private static int Clamp(int value, int min, int max)
		{
			return Math.Max(min, Math.Min(max, value));
		}

		private static float Lerp(float a, float b, float t)
		{
			return a + (b - a) * Math.Max(0f, Math.Min(1f, t));
		}
	}
	[Flags]
	internal enum PeerFeatureFlags
	{
		None = 0,
		Compression = 1,
		RpcAoi = 4
	}
	internal sealed class PeerFeatureState
	{
		public object Rpc;

		public object Socket;

		public long Uid;

		public bool RegisteredRpc;

		public bool HandshakeSent;

		public bool HandshakeReceived;

		public int RemoteProtocol;

		public PeerFeatureFlags RemoteCapabilities;

		public bool CompressionActive;

		public bool RpcAoiActive;

		public int CompressionFailures;

		public double LastHandshakeTime;
	}
	internal static class FeatureNegotiation
	{
		internal const int ProtocolVersion = 2;

		internal const int FeatureMagic = 1179536211;

		internal const string RpcName = "SkadiNet_Features";

		private static readonly object Lock = new object();

		private static readonly Dictionary<object, PeerFeatureState> ByRpc = new Dictionary<object, PeerFeatureState>();

		private static readonly Dictionary<object, PeerFeatureState> BySocket = new Dictionary<object, PeerFeatureState>();

		private static readonly Dictionary<long, PeerFeatureState> ByUid = new Dictionary<long, PeerFeatureState>();

		internal static PeerFeatureFlags LocalCapabilities => PeerFeatureFlags.Compression | PeerFeatureFlags.RpcAoi;

		internal static void Initialize()
		{
			lock (Lock)
			{
				ByRpc.Clear();
				BySocket.Clear();
				ByUid.Clear();
			}
		}

		internal static PeerFeatureState GetOrCreateByRpc(object rpc, long uid = 0L)
		{
			if (rpc == null)
			{
				return null;
			}
			lock (Lock)
			{
				if (!ByRpc.TryGetValue(rpc, out var value))
				{
					value = new PeerFeatureState
					{
						Rpc = rpc,
						Uid = uid
					};
					ByRpc[rpc] = value;
				}
				if (uid != 0L)
				{
					value.Uid = uid;
					ByUid[uid] = value;
				}
				object socketFromRpc = NetReflection.GetSocketFromRpc(rpc);
				if (socketFromRpc != null)
				{
					value.Socket = socketFromRpc;
					BySocket[socketFromRpc] = value;
				}
				return value;
			}
		}

		internal static PeerFeatureState GetBySocket(object socket)
		{
			if (socket == null)
			{
				return null;
			}
			lock (Lock)
			{
				BySocket.TryGetValue(socket, out var value);
				return value;
			}
		}

		internal static PeerFeatureState GetByUid(long uid)
		{
			lock (Lock)
			{
				ByUid.TryGetValue(uid, out var value);
				return value;
			}
		}

		internal static void ClearPeer(object peerOrRpc, long uid)
		{
			object obj = NetReflection.GetPeerRpc(peerOrRpc);
			if (obj == null && peerOrRpc != null && ReflectionCache.ZRpcType != null && ReflectionCache.ZRpcType.IsInstanceOfType(peerOrRpc))
			{
				obj = peerOrRpc;
			}
			object socketFromRpc = NetReflection.GetSocketFromRpc(obj);
			lock (Lock)
			{
				PeerFeatureState value = null;
				if (uid != 0L)
				{
					ByUid.TryGetValue(uid, out value);
				}
				if (value == null && obj != null)
				{
					ByRpc.TryGetValue(obj, out value);
				}
				if (value == null && socketFromRpc != null)
				{
					BySocket.TryGetValue(socketFromRpc, out value);
				}
				if (uid != 0L)
				{
					ByUid.Remove(uid);
				}
				if (obj != null)
				{
					ByRpc.Remove(obj);
				}
				if (socketFromRpc != null)
				{
					BySocket.Remove(socketFromRpc);
				}
				if (value != null)
				{
					if (value.Uid != 0L)
					{
						ByUid.Remove(value.Uid);
					}
					if (value.Rpc != null)
					{
						ByRpc.Remove(value.Rpc);
					}
					if (value.Socket != null)
					{
						BySocket.Remove(value.Socket);
					}
					RemoveState(ByRpc, value);
					RemoveState(BySocket, value);
					RemoveState(ByUid, value);
				}
			}
		}

		private static void RemoveState<TKey>(Dictionary<TKey, PeerFeatureState> map, PeerFeatureState state)
		{
			if (state == null || map.Count == 0)
			{
				return;
			}
			List<TKey> list = new List<TKey>();
			foreach (KeyValuePair<TKey, PeerFeatureState> item in map)
			{
				if (item.Value == state)
				{
					list.Add(item.Key);
				}
			}
			foreach (TKey item2 in list)
			{
				map.Remove(item2);
			}
		}

		internal static bool IsCompressionActiveForSocket(object socket)
		{
			if (!EffectiveConfig.CompressionEnabled)
			{
				return false;
			}
			PeerFeatureState bySocket = GetBySocket(socket);
			if (bySocket == null)
			{
				return false;
			}
			if (bySocket.CompressionFailures >= EffectiveConfig.CompressionDisableAfterFailures)
			{
				return false;
			}
			if (bySocket.HandshakeReceived)
			{
				return Supports(bySocket, PeerFeatureFlags.Compression);
			}
			return false;
		}

		internal static bool IsRpcAoiActiveForUid(long uid)
		{
			if (!EffectiveConfig.RpcAoiEnabled)
			{
				return false;
			}
			PeerFeatureState byUid = GetByUid(uid);
			if (byUid == null)
			{
				return true;
			}
			if (byUid.HandshakeReceived)
			{
				return Supports(byUid, PeerFeatureFlags.RpcAoi);
			}
			return true;
		}

		internal static void RecordCompressionFailure(object socket)
		{
			PeerFeatureState bySocket = GetBySocket(socket);
			if (bySocket != null)
			{
				bySocket.CompressionFailures++;
				bySocket.CompressionActive = false;
			}
		}

		internal static void OnNewConnection(object znetPeer)
		{
			if (ModConfig.Enabled.Value)
			{
				object peerRpc = NetReflection.GetPeerRpc(znetPeer);
				if (peerRpc != null)
				{
					NetReflection.TryGetPeerUid(znetPeer, out var uid);
					PeerFeatureState orCreateByRpc = GetOrCreateByRpc(peerRpc, uid);
					RegisterRpc(peerRpc, orCreateByRpc);
					SendHello(peerRpc, orCreateByRpc);
				}
			}
		}

		private static void RegisterRpc(object rpc, PeerFeatureState state)
		{
			if (rpc == null || state == null || state.RegisteredRpc)
			{
				return;
			}
			try
			{
				if (!(ReflectionCache.ZRpcRegisterGenericPackageMethod == null) && !(ReflectionCache.ZRpcType == null) && !(ReflectionCache.ZPackageType == null))
				{
					MethodInfo method = typeof(FeatureNegotiation).GetMethod("RPC_Features_Generic", BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType);
					Delegate @delegate = Delegate.CreateDelegate(typeof(Action<, >).MakeGenericType(ReflectionCache.ZRpcType, ReflectionCache.ZPackageType), method);
					ReflectionCache.ZRpcRegisterGenericPackageMethod.Invoke(rpc, new object[2] { "SkadiNet_Features", @delegate });
					state.RegisteredRpc = true;
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("Could not register SkadiNet_Features: " + ex.Message));
				}
			}
		}

		private static void SendHello(object rpc, PeerFeatureState state)
		{
			if (rpc == null || state == null || state.HandshakeSent)
			{
				return;
			}
			try
			{
				object obj = ZPackageTools.NewPackage();
				ZPackageTools.WriteInt(obj, 1179536211);
				ZPackageTools.WriteInt(obj, 2);
				ZPackageTools.WriteInt(obj, (int)LocalCapabilities);
				ZPackageTools.WriteString(obj, "1.0.0");
				ReflectionCache.ZRpcInvokeMethod?.Invoke(rpc, new object[2]
				{
					"SkadiNet_Features",
					new object[1] { obj }
				});
				state.HandshakeSent = true;
				state.LastHandshakeTime = Time.realtimeSinceStartupAsDouble;
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("Could not send SkadiNet feature handshake: " + ex.Message));
				}
			}
		}

		private static void RPC_Features_Generic<TRpc, TPkg>(TRpc rpc, TPkg pkg)
		{
			RPC_Features(rpc, pkg);
		}

		private static void RPC_Features(object rpc, object pkg)
		{
			try
			{
				PeerFeatureState orCreateByRpc = GetOrCreateByRpc(rpc, 0L);
				if (orCreateByRpc == null || pkg == null)
				{
					return;
				}
				int pos = ZPackageTools.GetPos(pkg);
				ZPackageTools.SetPos(pkg, 0);
				if (ZPackageTools.ReadInt(pkg) != 1179536211)
				{
					ZPackageTools.SetPos(pkg, pos);
					return;
				}
				int num = ZPackageTools.ReadInt(pkg);
				int remoteCapabilities = ZPackageTools.ReadInt(pkg);
				string text = ZPackageTools.ReadString(pkg);
				orCreateByRpc.RemoteProtocol = num;
				orCreateByRpc.RemoteCapabilities = (PeerFeatureFlags)remoteCapabilities;
				orCreateByRpc.HandshakeReceived = num >= 1;
				RefreshActiveFlags(orCreateByRpc);
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogDebug((object)$"SkadiNet feature handshake: protocol={num}, capabilities={orCreateByRpc.RemoteCapabilities}, version={text}, compression={orCreateByRpc.CompressionActive}, rpcAoi={orCreateByRpc.RpcAoiActive}");
				}
				if (!orCreateByRpc.HandshakeSent)
				{
					SendHello(rpc, orCreateByRpc);
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("SkadiNet feature handshake receive failed: " + ex.Message));
				}
			}
		}

		private static bool Supports(PeerFeatureState state, PeerFeatureFlags flag)
		{
			if (state != null)
			{
				return (state.RemoteCapabilities & flag) != 0;
			}
			return false;
		}

		private static void RefreshActiveFlags(PeerFeatureState state)
		{
			if (state != null)
			{
				state.CompressionActive = EffectiveConfig.CompressionEnabled && Supports(state, PeerFeatureFlags.Compression);
				state.RpcAoiActive = EffectiveConfig.RpcAoiEnabled && Supports(state, PeerFeatureFlags.RpcAoi);
			}
		}
	}
	[HarmonyPatch]
	internal static class ZNetOnNewConnectionFeatureHandshakePatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = ReflectionCache.ZNetType ?? AccessTools.TypeByName("ZNet");
			Type type2 = ReflectionCache.ZNetPeerType ?? AccessTools.TypeByName("ZNetPeer");
			return AccessTools.Method(type, "OnNewConnection", (!(type2 != null)) ? null : new Type[1] { type2 }, (Type[])null) ?? AccessTools.Method(type, "OnNewConnection", (Type[])null, (Type[])null);
		}

		private static void Postfix(object __0)
		{
			FeatureNegotiation.OnNewConnection(__0);
		}
	}
	internal static class FrameHitchDiagnostics
	{
		private struct NetworkSnapshot
		{
			public bool IsServer;

			public bool IsDedicated;

			public int ZNetPeers;

			public int ZdoPeers;

			public int QueueTotalBytes;

			public int QueueMaxBytes;

			public int QueueSoftPeers;

			public int QueueHardPeers;

			public int ZdoSectors;

			public int ZdoObjectsApprox;

			public bool ZdoObjectCountCapped;

			public long ManagedMemoryBytes;

			public override string ToString()
			{
				string text = (ZdoObjectCountCapped ? "+" : "");
				return $"network[server={IsServer} dedicated={IsDedicated} znetPeers={ZNetPeers} zdoPeers={ZdoPeers} " + $"queueTotal={FormatBytes(QueueTotalBytes)} queueMax={FormatBytes(QueueMaxBytes)} queueSoftPeers={QueueSoftPeers} queueHardPeers={QueueHardPeers} " + $"zdoSectors={ZdoSectors} zdoObjects~={ZdoObjectsApprox}{text} managedMem={FormatBytes(ManagedMemoryBytes)}]";
			}
		}

		private const double HitchThresholdSeconds = 0.12;

		private const double SevereHitchThresholdSeconds = 0.25;

		private const double SummaryIntervalSeconds = 10.0;

		private const double HitchLogCooldownSeconds = 0.75;

		private const int MaxZdoObjectsSnapshotCount = 50000;

		private static double _lastRealtime;

		private static double _lastHitchLogTime;

		private static double _nextSummaryTime;

		private static double _summarySeconds;

		private static int _summaryFrames;

		private static int _summaryHitches;

		private static int _summarySevereHitches;

		private static double _summaryMaxFrameSeconds;

		internal static void Update()
		{
			if (!DiagnosticsEnabled())
			{
				return;
			}
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			if (_lastRealtime <= 0.0)
			{
				_lastRealtime = realtimeSinceStartupAsDouble;
				_nextSummaryTime = realtimeSinceStartupAsDouble + 10.0;
				return;
			}
			double num = Math.Max(0.0, realtimeSinceStartupAsDouble - _lastRealtime);
			_lastRealtime = realtimeSinceStartupAsDouble;
			double num2 = Math.Max(Time.unscaledDeltaTime, num);
			_summaryFrames++;
			_summarySeconds += num2;
			if (num2 > _summaryMaxFrameSeconds)
			{
				_summaryMaxFrameSeconds = num2;
			}
			if (num2 >= 0.12)
			{
				_summaryHitches++;
			}
			if (num2 >= 0.25)
			{
				_summarySevereHitches++;
			}
			if (num2 >= 0.12 && realtimeSinceStartupAsDouble - _lastHitchLogTime >= 0.75)
			{
				_lastHitchLogTime = realtimeSinceStartupAsDouble;
				LogFrameSnapshot(num2, num, (num2 >= 0.25) ? "severe" : "mild");
			}
			if (realtimeSinceStartupAsDouble >= _nextSummaryTime)
			{
				LogSummary();
				_nextSummaryTime = realtimeSinceStartupAsDouble + 10.0;
			}
		}

		private static bool DiagnosticsEnabled()
		{
			try
			{
				return Plugin.Log != null && ModConfig.Enabled != null && ModConfig.Enabled.Value && ModConfig.DebugLogging != null && ModConfig.DebugLogging.Value;
			}
			catch
			{
				return false;
			}
		}

		private static void LogFrameSnapshot(double frameDelta, double realtimeDelta, string severity)
		{
			NetworkSnapshot networkSnapshot = CaptureNetworkSnapshot();
			string text = ZDOManSendSchedulerPatch.DescribeRecentState();
			string text2 = OwnershipManager.DescribeRecentState();
			Plugin.Log.LogInfo((object)($"Frame hitch {severity}: frameMs={frameDelta * 1000.0:F1} realtimeGapMs={realtimeDelta * 1000.0:F1} " + $"instantFps={SafeFps(frameDelta):F1} avgFps10s={AverageFps():F1} " + $"{networkSnapshot} {text} {text2} sliders[scheduler={Value(ModConfig.SchedulerThroughput)} payload={Value(ModConfig.PayloadReducerStrength)} " + $"compression={Value(ModConfig.CompressionAggression)} ownership={Value(ModConfig.OwnershipIntensity)} rpcAoi={Value(ModConfig.RpcAoiAggression)} " + $"stutterGuard={Value(ModConfig.ClientStutterGuardStrength)}]"));
		}

		private static void LogSummary()
		{
			if (_summaryFrames > 0)
			{
				NetworkSnapshot networkSnapshot = CaptureNetworkSnapshot();
				Plugin.Log.LogInfo((object)($"Frame diagnostics {10.0:F0}s: frames={_summaryFrames} avgFps={AverageFps():F1} " + $"maxFrameMs={_summaryMaxFrameSeconds * 1000.0:F1} hitches>={120.0:F0}ms={_summaryHitches} " + $"severe>={250.0:F0}ms={_summarySevereHitches} {networkSnapshot}"));
				_summaryFrames = 0;
				_summarySeconds = 0.0;
				_summaryHitches = 0;
				_summarySevereHitches = 0;
				_summaryMaxFrameSeconds = 0.0;
			}
		}

		private static double AverageFps()
		{
			if (!(_summarySeconds > 0.0))
			{
				return 0.0;
			}
			return (double)_summaryFrames / _summarySeconds;
		}

		private static double SafeFps(double frameDelta)
		{
			if (!(frameDelta > 0.0))
			{
				return 0.0;
			}
			return 1.0 / frameDelta;
		}

		private static int Value(ConfigEntry<int> entry)
		{
			try
			{
				return entry?.Value ?? 0;
			}
			catch
			{
				return 0;
			}
		}

		private static NetworkSnapshot CaptureNetworkSnapshot()
		{
			NetworkSnapshot networkSnapshot = default(NetworkSnapshot);
			networkSnapshot.IsServer = NetReflection.IsServer();
			networkSnapshot.IsDedicated = NetReflection.IsDedicatedServer();
			NetworkSnapshot snapshot = networkSnapshot;
			object zDOManInstance = ZdoReflection.ZDOManInstance;
			foreach (object item in ZdoReflection.EnumeratePeers(zDOManInstance))
			{
				snapshot.ZdoPeers++;
				int sendQueueSizeForPeer = NetReflection.GetSendQueueSizeForPeer(item);
				snapshot.QueueTotalBytes += sendQueueSizeForPeer;
				if (sendQueueSizeForPeer > snapshot.QueueMaxBytes)
				{
					snapshot.QueueMaxBytes = sendQueueSizeForPeer;
				}
				if (sendQueueSizeForPeer > EffectiveConfig.PeerQueueSoftLimitBytes)
				{
					snapshot.QueueSoftPeers++;
				}
				if (sendQueueSizeForPeer > EffectiveConfig.PeerQueueHardLimitBytes)
				{
					snapshot.QueueHardPeers++;
				}
			}
			foreach (object item2 in NetReflection.EnumerateZNetPeers())
			{
				_ = item2;
				snapshot.ZNetPeers++;
			}
			CountZdoSectors(zDOManInstance, ref snapshot);
			snapshot.ManagedMemoryBytes = GC.GetTotalMemory(forceFullCollection: false);
			return snapshot;
		}

		private static void CountZdoSectors(object zdoMan, ref NetworkSnapshot snapshot)
		{
			try
			{
				if (zdoMan == null || ReflectionCache.ZDOObjectsBySectorField == null)
				{
					return;
				}
				object value = ReflectionCache.ZDOObjectsBySectorField.GetValue(zdoMan);
				if (value == null)
				{
					return;
				}
				if (value is IDictionary dictionary)
				{
					snapshot.ZdoSectors = dictionary.Count;
					{
						foreach (object value2 in dictionary.Values)
						{
							int num = 50000 - snapshot.ZdoObjectsApprox;
							if (num <= 0)
							{
								snapshot.ZdoObjectCountCapped = true;
								break;
							}
							snapshot.ZdoObjectsApprox += CountEnumerable(value2, num, ref snapshot.ZdoObjectCountCapped);
						}
						return;
					}
				}
				if (!(value is IEnumerable enumerable))
				{
					return;
				}
				foreach (object item in enumerable)
				{
					snapshot.ZdoSectors++;
					int num2 = 50000 - snapshot.ZdoObjectsApprox;
					if (num2 <= 0)
					{
						snapshot.ZdoObjectCountCapped = true;
						break;
					}
					snapshot.ZdoObjectsApprox += CountSectorEntry(item, num2, ref snapshot.ZdoObjectCountCapped);
				}
			}
			catch
			{
			}
		}

		private static int CountSectorEntry(object entry, int cap, ref bool capped)
		{
			if (entry == null)
			{
				return 0;
			}
			try
			{
				object value = entry;
				Type type = entry.GetType();
				if (type.IsGenericType && type.FullName != null && type.FullName.StartsWith("System.Collections.Generic.KeyValuePair", StringComparison.Ordinal))
				{
					value = type.GetProperty("Value")?.GetValue(entry, null) ?? entry;
				}
				return CountEnumerable(value, cap, ref capped);
			}
			catch
			{
				return 0;
			}
		}

		private static int CountEnumerable(object value, int cap, ref bool capped)
		{
			if (value == null)
			{
				return 0;
			}
			if (value is ICollection collection)
			{
				if (collection.Count > cap)
				{
					capped = true;
				}
				return Math.Min(collection.Count, cap);
			}
			if (!(value is IEnumerable enumerable))
			{
				return 0;
			}
			int num = 0;
			foreach (object item in enumerable)
			{
				_ = item;
				num++;
				if (num >= cap)
				{
					capped = true;
					break;
				}
			}
			return num;
		}

		private static string FormatBytes(long bytes)
		{
			if (bytes >= 1073741824)
			{
				return $"{(float)bytes / 1.0737418E+09f:F2}GB";
			}
			if (bytes >= 1048576)
			{
				return $"{(float)bytes / 1048576f:F1}MB";
			}
			if (bytes >= 1024)
			{
				return $"{(float)bytes / 1024f:F1}KB";
			}
			return $"{bytes}B";
		}
	}
	internal static class GameplayReflection
	{
		internal static void Initialize()
		{
		}

		internal static object GetZdoFromNView(object nview)
		{
			if (nview == null)
			{
				return null;
			}
			try
			{
				return ReflectionCache.ZNetViewGetZDOMethod?.Invoke(nview, null) ?? ReflectionCache.ZNetViewZdoField?.GetValue(nview);
			}
			catch
			{
				return null;
			}
		}

		internal static object GetNViewFromCharacterLike(object instance)
		{
			if (instance == null)
			{
				return null;
			}
			try
			{
				return ReflectionCache.CharacterNViewField?.GetValue(instance);
			}
			catch
			{
			}
			try
			{
				return ReflectionCache.MonsterAINViewField?.GetValue(instance);
			}
			catch
			{
			}
			try
			{
				return ReflectionCache.CachedField(instance.GetType(), "m_nview")?.GetValue(instance);
			}
			catch
			{
			}
			return null;
		}

		internal static object GetZdoFromCharacterLike(object instance)
		{
			return GetZdoFromNView(GetNViewFromCharacterLike(instance));
		}

		internal static bool TryGetPlayerId(object player, out long id)
		{
			id = 0L;
			if (player == null || ReflectionCache.PlayerGetPlayerIDMethod == null)
			{
				return false;
			}
			try
			{
				return ReflectionCache.TryConvertToLong(ReflectionCache.PlayerGetPlayerIDMethod.Invoke(player, null), out id);
			}
			catch
			{
			}
			return false;
		}

		internal static bool LooksLikePlayer(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			Type type = obj.GetType();
			if (!(type.Name == "Player"))
			{
				if (ReflectionCache.PlayerType != null)
				{
					return ReflectionCache.PlayerType.IsAssignableFrom(type);
				}
				return false;
			}
			return true;
		}
	}
	internal static class ModConfig
	{
		private const string GeneralSection = "General";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> LockServerConfig;

		internal static ConfigEntry<bool> DebugLogging;

		internal static ConfigEntry<int> SchedulerThroughput;

		internal static ConfigEntry<int> PayloadReducerStrength;

		internal static ConfigEntry<int> OwnershipIntensity;

		internal static ConfigEntry<int> CompressionAggression;

		internal static ConfigEntry<int> RpcAoiAggression;

		internal static ConfigEntry<int> ClientStutterGuardStrength;

		internal static void Bind(ConfigFile config)
		{
			Enabled = ConfigSyncManager.Bind(config, "General", "Enabled", value: true, Description("Master switch.", 100), ConfigSyncScope.ServerSynced);
			LockServerConfig = ConfigSyncManager.LockServerConfig;
			DebugLogging = ConfigSyncManager.Bind(config, "General", "DebugLogging", value: false, Description("Diagnostic log output. Enable temporarily to inspect frame hitches, FPS, network queues, scheduler stalls, compression cost, and ownership scan cost; keep this off during normal live play.", 80), ConfigSyncScope.ClientLocal);
			SchedulerThroughput = ConfigSyncManager.Bind(config, "General", "SchedulerThroughput", 35, FeatureSliderDescription("0 disables the adaptive scheduler. 1 keeps package caps close to vanilla while using the gentlest adaptive scheduler; 35 is recommended; 50 is balanced; 100 favors lower latency, higher ZDO throughput, and faster lagging-peer backfill. Steam send-rate is fixed internally at 36 MB/s while SkadiNet is enabled.", 70), ConfigSyncScope.ServerSynced);
			PayloadReducerStrength = ConfigSyncManager.Bind(config, "General", "PayloadReducerStrength", 30, FeatureSliderDescription("0 disables the payload reducer. 1 favors sync fidelity; 50 is balanced; 100 applies stronger Vector3/Quaternion micro-update reduction.", 60), ConfigSyncScope.ServerSynced);
			CompressionAggression = ConfigSyncManager.Bind(config, "General", "CompressionAggression", 50, FeatureSliderDescription("0 disables negotiated package compression. 1 compresses only large/high-value packets; 50 is balanced; 100 considers smaller packets and smaller savings.", 50), ConfigSyncScope.ServerSynced);
			OwnershipIntensity = ConfigSyncManager.Bind(config, "General", "OwnershipIntensity", 45, FeatureSliderDescription("0 disables Profile A adaptive ownership, peer-quality gates, and combat owner hints. 1 is very conservative with low CPU, narrow candidate reach, weak hints, and forgiving peer quality; 45 is recommended; 50 is balanced; 100 scans farther/faster, uses stronger hints, and rejects poor ping/jitter candidates more aggressively.", 40), ConfigSyncScope.ServerSynced);
			ClientStutterGuardStrength = ConfigSyncManager.Bind(config, "General", "ClientStutterGuardStrength", 50, FeatureSliderDescription("0 disables the client stutter guard. 50 is the balanced default. 1 runs cleanup sooner under pressure; 100 protects longer against cleanup stutter.", 30), ConfigSyncScope.ClientLocal);
			RpcAoiAggression = ConfigSyncManager.Bind(config, "General", "RpcAoiAggression", 35, FeatureSliderDescription("0 disables RPC AoI. 35 is the conservative default. 1 keeps a larger radius for safe visual RPCs; 50 is balanced; 100 routes eligible visual RPCs to smaller local areas. Unknown, unresolved, global, animation, noise, and state-critical RPCs always use vanilla routing.", 20), ConfigSyncScope.ServerSynced);
		}

		private static ConfigDescription Description(string description, int order)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			});
		}

		private static ConfigDescription FeatureSliderDescription(string description, int order)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			return new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = order
				}
			});
		}
	}
	internal static class NetReflection
	{
		[CompilerGenerated]
		private sealed class <EnumerateZNetPeers>d__24 : IEnumerable<object>, IEnumerable, IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			private int <>l__initialThreadId;

			private IEnumerator <>7__wrap1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <EnumerateZNetPeers>d__24(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>7__wrap1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
					{
						<>1__state = -1;
						object zNetInstance = ZNetInstance;
						if (zNetInstance == null)
						{
							return false;
						}
						IEnumerable enumerable = null;
						try
						{
							enumerable = ReflectionCache.ZNetGetPeersMethod?.Invoke(zNetInstance, null) as IEnumerable;
						}
						catch
						{
						}
						if (enumerable == null)
						{
							try
							{
								enumerable = ReflectionCache.ZNetPeersField?.GetValue(zNetInstance) as IEnumerable;
							}
							catch
							{
							}
						}
						if (enumerable == null)
						{
							return false;
						}
						<>7__wrap1 = enumerable.GetEnumerator();
						<>1__state = -3;
						break;
					}
					case 1:
						<>1__state = -3;
						break;
					}
					if (<>7__wrap1.MoveNext())
					{
						object current = <>7__wrap1.Current;
						<>2__current = current;
						<>1__state = 1;
						return true;
					}
					<>m__Finally1();
					<>7__wrap1 = null;
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>7__wrap1 is IDisposable disposable)
				{
					disposable.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<object> IEnumerable<object>.GetEnumerator()
			{
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					return this;
				}
				return new <EnumerateZNetPeers>d__24(0);
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<object>)this).GetEnumerator();
			}
		}

		private static Func<object, object> _peerRpcGetter;

		private static Func<object, object> _peerUidGetter;

		private static Func<object, Vector3> _peerRefPosGetter;

		private static Func<object, object> _rpcSocketGetter;

		private static Func<object, object> _socketSendQueueSizeGetter;

		internal static Type ZNetType => ReflectionCache.ZNetType;

		internal static Type ZNetPeerType => ReflectionCache.ZNetPeerType;

		internal static Type ZRpcType => ReflectionCache.ZRpcType;

		internal static Type ZPackageType => ReflectionCache.ZPackageType;

		internal static object ZNetInstance => ReflectionCache.ZNetInstanceField?.GetValue(null);

		internal static void Initialize()
		{
			_peerRpcGetter = ReflectionDelegateFactory.BoxedFieldGetter(ReflectionCache.PeerRpcField);
			_peerUidGetter = ReflectionDelegateFactory.BoxedFieldGetter(ReflectionCache.PeerUidField);
			_peerRefPosGetter = ReflectionDelegateFactory.Vector3FieldGetter(ReflectionCache.PeerRefPosField);
			_rpcSocketGetter = ReflectionDelegateFactory.BoxedInstanceMethod(ReflectionCache.ZRpcGetSocketMethod);
			_socketSendQueueSizeGetter = ReflectionDelegateFactory.BoxedInstanceMethod(ReflectionCache.ZSteamSocketGetSendQueueSizeMethod);
		}

		internal static bool IsServer()
		{
			try
			{
				object zNetInstance = ZNetInstance;
				if (zNetInstance == null || ReflectionCache.ZNetIsServerMethod == null)
				{
					return false;
				}
				return (bool)ReflectionCache.ZNetIsServerMethod.Invoke(zNetInstance, null);
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsDedicatedServer()
		{
			try
			{
				object zNetInstance = ZNetInstance;
				if (zNetInstance == null || ReflectionCache.ZNetIsDedicatedMethod == null)
				{
					return false;
				}
				return (bool)ReflectionCache.ZNetIsDedicatedMethod.Invoke(zNetInstance, null);
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryGetPeerUid(object peerOrRpc, out long uid)
		{
			uid = 0L;
			if (peerOrRpc == null)
			{
				return false;
			}
			try
			{
				if (ReflectionCache.TryConvertToLong(TryGet(_peerUidGetter, peerOrRpc), out uid))
				{
					return true;
				}
				if (ReflectionCache.TryConvertToLong(ReflectionCache.CachedField(peerOrRpc.GetType(), "m_uid")?.GetValue(peerOrRpc), out uid))
				{
					return true;
				}
				return TryGetUidFromPeerObject(GetPeerRpc(peerOrRpc), out uid);
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryGetUidFromPeerObject(object peerOrRpc, out long uid)
		{
			uid = 0L;
			if (peerOrRpc == null)
			{
				return false;
			}
			try
			{
				return ReflectionCache.TryConvertToLong(ReflectionCache.CachedField(peerOrRpc.GetType(), "m_uid")?.GetValue(peerOrRpc), out uid);
			}
			catch
			{
				return false;
			}
		}

		internal static object GetPeerRpc(object peer)
		{
			if (peer == null)
			{
				return null;
			}
			try
			{
				object obj = ReflectionCache.CachedField(peer.GetType(), "m_rpc")?.GetValue(peer);
				if (obj != null)
				{
					return obj;
				}
				object obj2 = TryGet(_peerRpcGetter, peer);
				if (obj2 == null)
				{
					return null;
				}
				return ReflectionCache.CachedField(obj2.GetType(), "m_rpc")?.GetValue(obj2) ?? obj2;
			}
			catch
			{
				return null;
			}
		}

		internal static object GetSocketFromRpc(object rpc)
		{
			if (rpc == null)
			{
				return null;
			}
			try
			{
				return TryGet(_rpcSocketGetter, rpc) ?? ReflectionCache.ZRpcGetSocketMethod?.Invoke(rpc, null);
			}
			catch
			{
				return null;
			}
		}

		internal static Vector3 GetPeerRefPos(object peer)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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)
			if (peer == null)
			{
				return Vector3.zero;
			}
			try
			{
				if (TryGetVector3(_peerRefPosGetter, peer, out var value))
				{
					return value;
				}
				if (ReflectionCache.CachedField(peer.GetType(), "m_refPos")?.GetValue(peer) is Vector3 result)
				{
					return result;
				}
				object obj = ReflectionCache.CachedField(peer.GetType(), "m_peer")?.GetValue(peer);
				if (obj != null && obj != peer)
				{
					object obj2 = ReflectionCache.CachedField(obj.GetType(), "m_refPos")?.GetValue(obj);
					if (obj2 is Vector3)
					{
						return (Vector3)obj2;
					}
				}
			}
			catch
			{
			}
			return Vector3.zero;
		}

		internal static int GetSendQueueSizeForPeer(object zdoPeer)
		{
			try
			{
				object peerRpc = GetPeerRpc(zdoPeer);
				if (peerRpc == null)
				{
					return 0;
				}
				object socketFromRpc = GetSocketFromRpc(peerRpc);
				if (socketFromRpc == null)
				{
					return 0;
				}
				object obj = TryGet(_socketSendQueueSizeGetter, socketFromRpc) ?? ReflectionCache.ZSteamSocketGetSendQueueSizeMethod?.Invoke(socketFromRpc, null) ?? AccessTools.Method(socketFromRpc.GetType(), "GetSendQueueSize", (Type[])null, (Type[])null)?.Invoke(socketFromRpc, null);
				if (obj is int result)
				{
					return result;
				}
				if (obj is long)
				{
					long val = (long)obj;
					return (int)Math.Min(2147483647L, val);
				}
			}
			catch
			{
			}
			return 0;
		}

		[IteratorStateMachine(typeof(<EnumerateZNetPeers>d__24))]
		internal static IEnumerable<object> EnumerateZNetPeers()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <EnumerateZNetPeers>d__24(-2);
		}

		internal static Vector3 GetReferencePosition(Vector3 fallback)
		{
			//IL_003b: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object zNetInstance = ZNetInstance;
				if (zNetInstance != null && ReflectionCache.ZNetGetReferencePositionMethod != null)
				{
					object obj = ReflectionCache.ZNetGetReferencePositionMethod.Invoke(zNetInstance, null);
					if (obj is Vector3)
					{
						return (Vector3)obj;
					}
				}
			}
			catch
			{
			}
			return fallback;
		}

		private static object TryGet(Func<object, object> getter, object instance)
		{
			try
			{
				return (getter != null && instance != null) ? getter(instance) : null;
			}
			catch
			{
				return null;
			}
		}

		private static bool TryGetVector3(Func<object, Vector3> getter, object instance, out Vector3 value)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			value = Vector3.zero;
			try
			{
				if (getter == null || instance == null)
				{
					return false;
				}
				value = getter(instance);
				return true;
			}
			catch
			{
				return false;
			}
		}
	}
	internal enum OwnershipCandidateReason
	{
		Generic,
		CombatTarget,
		DisconnectedOwner,
		LongUnownedPersistent
	}
	internal sealed class OwnerState
	{
		public long CurrentOwner;

		public long PreviousOwner;

		public double LastOwnerChangeTime;

		public double LastCombatOwnerChangeTime;

		public double LastSeenUnownedTime;

		public double LastTouchedTime;

		public int ChangeCount;

		public long CombatTargetUid;

		public double CombatTargetHintTime;
	}
	internal sealed class OwnerCandidate
	{
		public long Uid;

		public object Peer;

		public float Distance;

		public float Quality;

		public float Score;

		public int EstimatedLoad;

		public bool IsCombatTarget;
	}
	internal static class OwnershipManager
	{
		private const int MaxOwnerStates = 50000;

		private const double OwnerStateTtlSeconds = 600.0;

		private const double OwnerStatePruneIntervalSeconds = 30.0;

		private const bool AllowShipOwnership = false;

		private const bool AllowHealthyOwnerChallenge = false;

		private const bool AllowServerFallbackForPersistentRecovery = true;

		private static readonly Dictionary<ZdoIdKey, OwnerState> ByZdoId = new Dictionary<ZdoIdKey, OwnerState>();

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

		private static double _nextProfileAScan;

		private static double _nextOwnerStatePrune;

		private static int _scanCursor;

		private static int _sectorCursor;

		private static double _lastScanTime;

		private static double _lastScanSeconds;

		private static int _lastScanVisited;

		private static int _lastScanBudget;

		private static int _lastScanOwnerChanges;

		private static int _lastScanPeerCount;

		private static int _lastScanSectorCursor;

		private static bool _lastScanSkippedNoPeers;

		internal static bool ProfileAEnabled
		{
			get
			{
				if (!EffectiveConfig.AdaptiveOwnershipEnabled || !NetReflection.IsServer())
				{
					return false;
				}
				return true;
			}
		}

		internal static void Initialize()
		{
			ByZdoId.Clear();
			OwnerLoadEstimate.Clear();
			_nextProfileAScan = 0.0;
			_nextOwnerStatePrune = 0.0;
			_scanCursor = 0;
			_sectorCursor = 0;
			_lastScanTime = 0.0;
			_lastScanSeconds = 0.0;
			_lastScanVisited = 0;
			_lastScanBudget = 0;
			_lastScanOwnerChanges = 0;
			_lastScanPeerCount = 0;
			_lastScanSectorCursor = 0;
			_lastScanSkippedNoPeers = false;
		}

		internal static bool TryTransferCombatOwnership(object monsterAI, object target)
		{
			if (!ProfileAEnabled || !EffectiveConfig.OwnerHintsEnabled)
			{
				return false;
			}
			if (!GameplayReflection.LooksLikePlayer(target))
			{
				return false;
			}
			if (!GameplayReflection.TryGetPlayerId(target, out var id) || id == 0L)
			{
				return false;
			}
			object zdoFromCharacterLike = GameplayReflection.GetZdoFromCharacterLike(monsterAI);
			if (zdoFromCharacterLike == null)
			{
				return false;
			}
			OwnerState ownerState = GetOwnerState(zdoFromCharacterLike);
			ownerState.CombatTargetUid = id;
			ownerState.CombatTargetHintTime = Time.realtimeSinceStartupAsDouble;
			return TryMaybeImproveOwner(zdoFromCharacterLike, OwnershipCandidateReason.CombatTarget);
		}

		internal static void ClearPeer(long uid)
		{
			if (uid == 0L)
			{
				return;
			}
			OwnerLoadEstimate.Remove(uid);
			foreach (OwnerState value in ByZdoId.Values)
			{
				if (value.CombatTargetUid == uid)
				{
					value.CombatTargetUid = 0L;
					value.CombatTargetHintTime = 0.0;
				}
			}
		}

		internal static void TickLightweight(object zdoMan)
		{
			if (!ProfileAEnabled)
			{
				return;
			}
			if (!HasAnyZdoPeer(zdoMan, out var peerCount))
			{
				_lastScanSkippedNoPeers = true;
				_lastScanPeerCount = 0;
				return;
			}
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			PruneOwnerStatesIfDue(realtimeSinceStartupAsDouble);
			if (!(realtimeSinceStartupAsDouble < _nextProfileAScan))
			{
				_nextProfileAScan = realtimeSinceStartupAsDouble + (double)Math.Max(0.1f, EffectiveConfig.OwnershipScanIntervalSeconds);
				_lastScanPeerCount = peerCount;
				TryProfileAScan(zdoMan);
			}
		}

		internal static string DescribeRecentState()
		{
			double num = ((_lastScanTime > 0.0) ? Math.Max(0.0, Time.realtimeSinceStartupAsDouble - _lastScanTime) : (-1.0));
			string text = ((num >= 0.0) ? $"{num:F2}s" : "n/a");
			return $"ownershipRecent age={text} scanMs={_lastScanSeconds * 1000.0:F2} visited={_lastScanVisited}/{_lastScanBudget} " + $"ownerChanges={_lastScanOwnerChanges} zdoPeers={_lastScanPeerCount} sectorCursor={_lastScanSectorCursor} " + $"skippedNoPeers={_lastScanSkippedNoPeers}";
		}

		private static bool HasAnyZdoPeer(object zdoMan, out int peerCount)
		{
			peerCount = 0;
			try
			{
				if (zdoMan == null || ReflectionCache.ZDOManPeersField == null)
				{
					return false;
				}
				object value = ReflectionCache.ZDOManPeersField.GetValue(zdoMan);
				if (value is ICollection collection)
				{
					peerCount = collection.Count;
					return peerCount > 0;
				}
				if (value is IEnumerable enumerable)
				{
					{
						IEnumerator enumerator = enumerable.GetEnumerator();
						try
						{
							if (enumerator.MoveNext())
							{
								_ = enumerator.Current;
								peerCount++;
								return true;
							}
						}
						finally
						{
							IDisposable disposable = enumerator as IDisposable;
							if (disposable != null)
							{
								disposable.Dispose();
							}
						}
					}
				}
			}
			catch
			{
			}
			return false;
		}

		private static void TryProfileAScan(object zdoMan)
		{
			try
			{
				double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
				OwnerLoadEstimate.Clear();
				object obj = ReflectionCache.ZDOObjectsBySectorField?.GetValue(zdoMan);
				if (!(obj is IEnumerable enumerable))
				{
					return;
				}
				int visited = 0;
				int acted = 0;
				int num = Math.Max(1, EffectiveConfig.OwnershipScanBudget);
				if (obj is IList list && list.Count > 0)
				{
					if (_sectorCursor < 0 || _sectorCursor >= list.Count)
					{
						_sectorCursor = 0;
					}
					int sectorCursor = _sectorCursor;
					int num2 = 0;
					while (true)
					{
						if (num2 < list.Count)
						{
							int num3 = (sectorCursor + num2) % list.Count;
							if (ScanBucket(list[num3], num, ref visited, ref acted))
							{
								_sectorCursor = (num3 + 1) % list.Count;
								break;
							}
							num2++;
							continue;
						}
						_sectorCursor = 0;
						break;
					}
				}
				else
				{
					{
						IEnumerator enumerator = enumerable.GetEnumerator();
						try
						{
							while (enumerator.MoveNext() && !ScanBucket(enumerator.Current, num, ref visited, ref acted))
							{
							}
						}
						finally
						{
							IDisposable disposable = enumerator as IDisposable;
							if (disposable != null)
							{
								disposable.Dispose();
							}
						}
					}
				}
				double num4 = Time.realtimeSinceStartupAsDouble - realtimeSinceStartupAsDouble;
				_lastScanTime = Time.realtimeSinceStartupAsDouble;
				_lastScanSeconds = num4;
				_lastScanVisited = visited;
				_lastScanBudget = num;
				_lastScanOwnerChanges = acted;
				_lastScanSectorCursor = _sectorCursor;
				_lastScanSkippedNoPeers = false;
				if (ModConfig.DebugLogging.Value && (acted > 0 || visited > 0))
				{
					bool flag = num4 >= 0.008;
					if (acted > 0 || flag)
					{
						Plugin.Log.LogInfo((object)$"Adaptive ownership scan: elapsed={num4 * 1000.0:F2}ms slow={flag} visited={visited}/{num}, ownerChanges={acted}, zdoPeers={_lastScanPeerCount}, loadOwners={OwnerLoadEstimate.Count}, sectorCursor={_sectorCursor}, stride={EffectiveConfig.OwnershipScanStride}");
					}
				}
			}
			catch (Exception ex)
			{
				if (ModConfig.DebugLogging.Value)
				{
					Plugin.Log.LogWarning((object)("Adaptive ownership scan failed: " + ex.Message));
				}
			}
		}

		private static bool ScanBucket(object bucket, int budget, ref int visited, ref int acted)
		{
			if (!(bucket is IEnumerable enumerable))
			{
				return false;
			}
			foreach (object item in enumerable)
			{
				if (item == null)
				{
					continue;
				}
				_scanCursor++;
				int num = Math.Max(1, EffectiveConfig.OwnershipScanStride);
				if (_scanCursor % num == 0)
				{
					TrackCurrentOwnerLoad(item);
					visited++;
					if (TryMaybeImproveOwner(item, OwnershipCandidateReason.Generic))
					{
						acted++;
					}
					if (visited >= budget)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static void TrackCurrentOwnerLoad(object zdo)
		{
			if (ZdoReflection.TryGetOwner(zdo, out var owner) && owner != 0L)
			{
				OwnerLoadEstimate.TryGetValue(owner, out var value);
				OwnerLoadEstimate[owner] = value + 1;
			}
		}

		private static bool TryMaybeImproveOwner(object zdo, OwnershipCandidateReason reason)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			if (zdo == null || ReflectionCache.ZDOGetPositionMethod == null)
			{
				return false;
			}
			if (!ZdoReflection.TryGetOwner(zdo, out var owner))
			{
				owner = 0L;
			}
			bool persistent;
			bool flag = ZdoReflection.TryGetPersistent(zdo, out persistent) && persistent;
			bool num = ZdoKeyPolicy.LooksPlayerLike(zdo);
			bool flag2 = ZdoKeyPolicy.LooksShipLike(zdo);
			if (num)
			{
				return false;
			}
			if (flag2 && reason != OwnershipCandidateReason.DisconnectedOwner)
			{
				return false;
			}
			OwnerState ownerState = GetOwnerState(zdo);
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			bool flag3 = owner != 0L && IsUidCurrentlyConnected(owner);
			long uid;
			bool flag4 = owner != 0L && ZdoReflection.TryGetServerSessionId(out uid) && owner == uid;
			if (owner == 0L)
			{
				if (ownerState.LastSeenUnownedTime <= 0.0)
				{
					ownerState.LastSeenUnownedTime = realtimeSinceStartupAsDouble;
				}
				if (flag && realtimeSinceStartupAsDouble - ownerState.LastSeenUnownedTime >= (double)EffectiveConfig.RecoverUnownedAfterSeconds)
				{
					reason = OwnershipCandidateReason.LongUnownedPersistent;
				}
			}
			else
			{
				ownerState.LastSeenUnownedTime = 0.0;
			}
			if (flag && reason != OwnershipCandidateReason.LongUnownedPersistent && reason != OwnershipCandidateReason.DisconnectedOwner)
			{
				return false;
			}
			if (!flag3 && owner != 0L && !flag4)
			{
				reason = OwnershipCandidateReason.DisconnectedOwner;
			}
			Vector3 position = ZdoReflection.GetPosition(zdo, Vector3.zero);
			if (reason == OwnershipCandidateReason.Generic && flag3)
			{
				return false;
			}
			OwnerCandidate ownerCandidate = FindBestCandidate(zdo, position, ownerState, reason);
			if (ownerCandidate == null)
			{
				if (reason == OwnershipCandidateReason.LongUnownedPersistent)
				{
					return RecoverToServerOwner(zdo, ownerState, owner, reason);
				}
				return false;
			}
			if (ownerCandidate.Uid == owner)
			{
				return false;
			}
			float num2 = ComputeCurrentOwnerScore(owner, position, ownerState, reason, flag3, flag4);
			if (!IsCandidateBetter(ownerCandidate.Score, num2, reason))
			{
				return false;
			}
			float num3 = ((reason == OwnershipCandidateReason.CombatTarget) ? EffectiveConfig.OwnerHintSwitchCooldownSeconds : EffectiveConfig.OwnerSwitchCooldownSeconds);
			if (flag2)
			{
				num3 = Math.Max(num3, EffectiveConfig.ShipOwnerSwitchCooldownSeconds);
			}
			if (realtimeSinceStartupAsDouble - ownerState.LastOwnerChangeTime < (double)num3)
			{
				return false;
			}
			if (reason == OwnershipCandidateReason.CombatTarget && realtimeSinceStartupAsDouble - ownerState.LastCombatOwnerChangeTime < (double)EffectiveConfig.OwnerHintSwitchCooldownSeconds)
			{
				return false;
			}
			if (!ZdoReflection.TrySetOwner(zdo, ownerCandidate.Uid))
			{
				return false;
			}
			ownerState.PreviousOwner = owner;
			ownerState.CurrentOwner = ownerCandidate.Uid;
			ownerState.LastOwnerChangeTime = realtimeSinceStartupAsDouble;
			if (reason == OwnershipCandidateReason.CombatTarget)
			{
				ownerState.LastCombatOwnerChangeTime = realtimeSinceStartupAsDouble;
			}
			ownerState.ChangeCount++;
			ZdoReflection.ForceSend(zdo);
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"ProfileA owner {reason}: {owner}->{ownerCandidate.Uid}, best={ownerCandidate.Score:F1}, current={num2:F1}, q={ownerCandidate.Quality:F1}, dist={ownerCandidate.Distance:F1}, load={ownerCandidate.EstimatedLoad}");
			}
			return true;
		}

		private static OwnerCandidate FindBestCandidate(object zdo, Vector3 zdoPosition, OwnerState state, OwnershipCandidateReason reason)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			OwnerCandidate ownerCandidate = null;
			float num = ((reason == OwnershipCandidateReason.CombatTarget) ? Math.Max(EffectiveConfig.OwnershipCandidateRadius, EffectiveConfig.OwnerHintCandidateRadius) : EffectiveConfig.OwnershipCandidateRadius);
			foreach (object item in ZdoReflection.EnumeratePeers(ZdoReflection.ZDOManInstance))
			{
				if (!NetReflection.TryGetPeerUid(item, out var uid) || uid == 0L)
				{
					continue;
				}
				Vector3 peerRefPos = NetReflection.GetPeerRefPos(item);
				float num2 = Vector3.Distance(zdoPosition, peerRefPos);
				if (num2 > num)
				{
					continue;
				}
				PeerQualityState peerQualityState = PeerQualityMeter.UpdateFromPeer(item) ?? PeerQualityMeter.GetByUid(uid) ?? PeerQualityMeter.GetByPeer(item);
				if (CandidateConnectionAllowed(peerQualityState, reason))
				{
					int num3 = EstimateOwnerLoad(uid);
					float num4 = ComputeCandidateScore(uid, num2, peerQualityState, num3, state, reason);
					if (ownerCandidate == null || num4 < ownerCandidate.Score)
					{
						ownerCandidate = new OwnerCandidate
						{
							Uid = uid,
							Peer = item,
							Distance = num2,
							Quality = (peerQualityState?.ConnectionQualityMs ?? 999f),
							Score = num4,
							EstimatedLoad = num3,
							IsCombatTarget = (uid == state.CombatTargetUid && IsCombatHintFresh(state))
						};
					}
				}
			}
			return ownerCandidate;
		}

		private static float ComputeCandidateScore(long uid, float distance, PeerQualityState quality, int load, OwnerState state, OwnershipCandidateReason reason)
		{
			float num = quality?.ConnectionQualityMs ?? 999f;
			num += distance * Math.Max(0f, EffectiveConfig.OwnershipDistanceScoreWeight);
			num += (float)load * Math.Max(0f, EffectiveConfig.OwnershipLoadPenaltyPerZdo);
			if (uid == state.CombatTargetUid && IsCombatHintFresh(state))
			{
				num -= Math.Max(0f, EffectiveConfig.OwnerHintScoreBonusMs);
			}
			return num;
		}

		private static float ComputeCurrentOwnerScore(long currentOwner, Vector3 zdoPosition, OwnerState state, OwnershipCandidateReason reason, bool currentConnected, bool currentIsServer)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (currentOwner == 0L)
			{
				return 999f;
			}
			if (!currentConnected && !currentIsServer)
			{
				return 999f;
			}
			if (currentIsServer)
			{
				return EffectiveConfig.ServerFallbackPenaltyMs;
			}
			PeerQualityState byUid = PeerQualityMeter.GetByUid(currentOwner);
			object obj = FindZdoPeerByUid(currentOwner);
			float distance = ((obj != null) ? Vector3.Distance(zdoPosition, NetReflection.GetPeerRefPos(obj)) : 0f);
			return ComputeCandidateScore(currentOwner, distance, byUid, EstimateOwnerLoad(currentOwner), state, reason);
		}

		private static bool RecoverToServerOwner(object zdo, OwnerState state, long currentOwner, OwnershipCandidateReason reason)
		{
			if (!ZdoReflection.TryGetServerSessionId(out var uid) || uid == 0L)
			{
				return false;
			}
			if (!ZdoReflection.TrySetOwner(zdo, uid))
			{
				return false;
			}
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			state.PreviousOwner = currentOwner;
			state.CurrentOwner = uid;
			state.LastOwnerChangeTime = realtimeSinceStartupAsDouble;
			state.ChangeCount++;
			ZdoReflection.ForceSend(zdo);
			if (ModConfig.DebugLogging.Value)
			{
				Plugin.Log.LogDebug((object)$"ProfileA recovery to server owner {reason}: {currentOwner}->{uid}");
			}
			return true;
		}

		private static int EstimateOwnerLoad(long uid)
		{
			if (uid == 0L)
			{
				return 0;
			}
			if (OwnerLoadEstimate.TryGetValue(uid, out var value))
			{
				return value;
			}
			return PeerQualityMeter.GetByUid(uid)?.OwnedDynamicEstimate ?? 0;
		}

		private static bool CandidateConnectionAllowed(PeerQualityState candidate, OwnershipCandidateReason reason)
		{
			if (!EffectiveConfig.PeerQualityEnabled)
			{
				return true;
			}
			if (candidate == null || !candidate.HasAnySample)
			{
				return false;
			}
			if (candidate.PingEmaMs > EffectiveConfig.MaxCandidatePingMs)
			{
				return false;
			}
			if (candidate.PingJitterMs > EffectiveConfig.MaxCandidateJitterMs)
			{
				return false;
			}
			return true;
		}

		private static bool IsCandidateBetter(float candidateScore, float currentScore, OwnershipCandidateReason reason)
		{
			if (currentScore <= 0f || currentScore >= 900f)
			{
				return true;
			}
			float num = Math.Max(0f, EffectiveConfig.OwnershipRelativeHysteresis);
			float val = Math.Max(0f, EffectiveConfig.OwnershipAbsoluteHysteresisMs);
			if (reason == OwnershipCandidateReason.DisconnectedOwner || reason == OwnershipCandidateReason.LongUnownedPersistent)
			{
				val = Math.Min(val, 5f);
			}
			float num2 = Math.Max(val, currentScore * num);
			return currentScore - candidateScore >= num2;
		}

		private static OwnerState GetOwnerState(object zdo)
		{
			if (!ZdoReflection.TryGetIdKey(zdo, out var key))
			{
				key = ZdoIdKey.FromRuntimeObject(zdo);
			}
			if (!ByZdoId.TryGetValue(key, out var value))
			{
				value = new OwnerState();
				ByZdoId[key] = value;
			}
			value.LastTouchedTime = Time.realtimeSinceStartupAsDouble;
			return value;
		}

		private static void PruneOwnerStatesIfDue(double now)
		{
			if (now < _nextOwnerStatePrune)
			{
				return;
			}
			_nextOwnerStatePrune = now + 30.0;
			List<ZdoIdKey> list = new List<ZdoIdKey>();
			foreach (KeyValuePair<ZdoIdKey, OwnerState> item in ByZdoId)
			{
				OwnerState value = item.Value;
				if (value == null || (value.LastTouchedTime > 0.0 && now - value.LastTouchedTime >= 600.0))
				{
					list.Add(item.Key);
				}
			}
			foreach (ZdoIdKey item2 in list)
			{
				ByZdoId.Remove(item2);
			}
			while (ByZdoId.Count > 50000 && RemoveOldestOwnerState())
			{
			}
		}

		private static bool RemoveOldestOwnerState()
		{
			ZdoIdKey key = default(ZdoIdKey);
			double num = double.MaxValue;
			bool flag = false;
			foreach (KeyValuePair<ZdoIdKey, OwnerState> item in ByZdoId)
			{
				double num2 = item.Value?.LastTouchedTime ?? 0.0;
				if (num2 < num)
				{
					num = num2;
					key = item.Key;
					flag = true;
				}
			}
			if (!flag)
			{
				return false;
			}
			ByZdoId.Remove(key);
			return true;
		}

		private static bool IsCombatHintFresh(OwnerState state)
		{
			if (state == null || state.CombatTargetUid == 0L)
			{
				return false;
			}
			return Time.realtimeSinceStartupAsDouble - state.CombatTargetHintTime <= (double)Math.Max(0.1f, EffectiveConfig.OwnerHintLifetimeSeconds);
		}

		private static bool IsUidCurrentlyConnected(long uid)
		{
			return FindZdoPeerByUid(uid) != null;
		}

		private static object FindZdoPeerByUid(long uid)
		{
			if (uid == 0L)
			{
				return null;
			}
			foreach (object item in ZdoReflection.EnumeratePeers(ZdoReflection.ZDOManInstance))
			{
				if (NetReflection.TryGetPeerUid(item, out var uid2) && uid2 == uid)
				{
					return item;
				}
			}
			return null;
		}
	}
	internal enum PayloadCullValueKind : byte
	{
		Vector3,
		Quaternion
	}
	internal readonly struct PayloadCullKey : IEquatable<PayloadCullKey>
	{
		private readonly ZdoIdKey _zdoId;

		private readonly int _hash;

		private readonly PayloadCullValueKind _kind;

		internal PayloadCullKey(ZdoIdKey zdoId, int hash, PayloadCullValueKind kind)
		{
			_zdoId = zdoId;
			_hash = hash;
			_kind = kind;
		}

		public bool Equals(PayloadCullKey other)
		{
			if (_zdoId.Equals(other._zdoId) && _hash == other._hash)
			{
				return _kind == other._kind;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is PayloadCullKey other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (_zdoId.GetHashCode() * 31 + _hash) * 31 + (int)_kind;
		}
	}
	internal static class PayloadCullState
	{
		private const int MaxEntries = 65536;

		private const double MinEntryTtlSeconds = 60.0;

		private const double PruneIntervalSeconds = 30.0;

		private static readonly Dictionary<PayloadCullKey, double> LastAllowedByZdoKey = new Dictionary<PayloadCullKey, double>();

		private static readonly object Lock = new object();

		private static double _nextPruneTime;

		internal static bool ShouldAllowByTime(object zdo, int hash, PayloadCullValueKind kind)
		{
			float num = Math.Max(0.05f, EffectiveConfig.PayloadForceRefreshSeconds);
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			PayloadCullKey key = BuildKey(zdo, hash, kind);
			lock (Lock)
			{
				PruneIfDue(realtimeSinceStartupAsDouble);
				if (!LastAllowedByZdoKey.TryGetValue(key, out var value) || realtimeSinceStartupAsDouble - value >= (double)num)
				{
					LastAllowedByZdoKey[key] = realtimeSinceStartupAsDouble;
					return true;
				}
			}
			return false;
		}

		internal static void MarkAllowed(object zdo, int hash, PayloadCullValueKind kind)
		{
			PayloadCullKey key = BuildKey(zdo, hash, kind);
			double realtimeSinceStartupAsDouble = Time.realtimeSinceStartupAsDouble;
			lock (Lock)
			{
				PruneIfDue(realtimeSinceStartupAsDouble);
				LastAllowedByZdoKey[key] = realtimeSinceStartupAsDouble;
			}
		}

		private static void PruneIfDue(double now)
		{
			if (now < _nextPruneTime)
			{
				return;
			}
			_nextPruneTime = now + 30.0;
			double num = Math.Max(60.0, (double)Math.Max(0.05f, EffectiveConfig.PayloadForceRefreshSeconds) * 20.0);
			List<PayloadCullKey> list = new List<PayloadCullKey>();
			foreach (KeyValuePair<PayloadCullKey, double> item in LastAllowedByZdoKey)
			{
				if (now - item.Value >= num)
				{
					list.Add(item.Key);
				}
			}
			foreach (PayloadCullKey item2 in list)
			{
				LastAllowedByZdoKey.Remove(item2);
			}
			while (LastAllowedByZdoKey.Count > 65536 && RemoveOldest())
			{
			}
		}

		private static bool RemoveOldest()
		{
			PayloadCullKey key = default(PayloadCullKey);
			double num = double.MaxValue;
			bool flag = false;
			foreach (KeyValuePair<PayloadCullKey, double> item in LastAllowedByZdoKey)
			{
				if (item.Value < num)
				{
					num = item.Value;
					key = item.Key;
					flag = true;
				}
			}
			if (!flag)
			{
				return false;
			}
			LastAllowedByZdoKey.Remove(key);
			return true;
		}

		private static PayloadCullKey BuildKey(object zdo, int hash, PayloadCullValueKind kind)
		{
			if (!ZdoReflection.TryGetIdKey(zdo, out var key))
			{
				key = ZdoIdKey.FromRuntimeObject(zdo);
			}
			return new PayloadCullKey(key, hash, kind);
		}
	}
	[HarmonyPatch]
	internal static class ZDOSetVector3ReducerPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(ReflectionCache.ZDOType ?? AccessTools.TypeByName("ZDO"), "Set", new Type[2]
			{
				typeof(int),
				typeof(Vector3)
			}, (Type[])null);
		}

		private static bool Prefix(object __instance, int __0, Vector3 __1)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			i