Decompiled source of VBNetTweaks v0.4.3

VBNetTweaks.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using VBNetTweaks.Patches;
using VBNetTweaks.Utils;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("VBNetTweaks")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("VitByr")]
[assembly: AssemblyProduct("VBNetTweaks")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("d9c87954-ce20-459d-81ec-96599caed427")]
[assembly: AssemblyFileVersion("0.4.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace VBNetTweaks
{
	[BepInPlugin("VitByr.VBNetTweaks", "VBNetTweaks", "0.4.3.5")]
	[BepInIncompatibility("CacoFFF.valheim.LeanNet")]
	[BepInIncompatibility("redseiko.valheim.scenic")]
	[BepInIncompatibility("Searica.Valheim.NetworkTweaks")]
	[BepInIncompatibility("Searica.Valheim.OpenSesame")]
	[BepInIncompatibility("org.bepinex.plugins.network")]
	[BepInIncompatibility("CW_Jesse.BetterNetworking")]
	[BepInIncompatibility("com.Fire.FiresGhettoNetworkMod")]
	[BepInIncompatibility("sighsorry.SkadiNet")]
	[BepInIncompatibility("redseiko.valheim.returntosender")]
	[BepInIncompatibility("com.maxsch.valheim.TimeoutLimit")]
	[BepInIncompatibility("dzk.warheimnetwork")]
	[BepInIncompatibility("r4v9n1.terramizerserver")]
	[BepInIncompatibility("com.bfv.CombatPrio")]
	public class VBNetTweaks : BaseUnityPlugin
	{
		private const string ModName = "VBNetTweaks";

		private const string ModVersion = "0.4.3.5";

		private const string ModGUID = "VitByr.VBNetTweaks";

		public CustomRPC _configSyncRPC;

		private ConfigFile _clientConfig;

		public static ManualLogSource Logger;

		public static ConfigEntry<Language> c_ConfigLanguage;

		public static ConfigEntry<bool> c_ModEnabled;

		public static ConfigEntry<bool> c_DebugEnabled;

		public static ConfigEntry<bool> c_VerboseLogging;

		public static ConfigEntry<bool> c_NetStatsLogging;

		public static ConfigEntry<int> c_NetStatsInterval;

		public static ConfigEntry<int> c_TargetFPS;

		public static ConfigEntry<bool> c_ModuleSteamOptimizations;

		public static ConfigEntry<bool> c_ModuleZDOOptimization;

		public static ConfigEntry<bool> c_ModuleMapPositionSync;

		public static ConfigEntry<bool> c_ModuleStutterGuard;

		public static ConfigEntry<int> c_SteamSendRateMaxKB;

		public static ConfigEntry<int> c_SteamSendBufferSizeKB;

		public static ConfigEntry<float> c_SteamTimeoutConnected;

		public static ConfigEntry<float> c_MapPositionSendInterval;

		public static ConfigEntry<float> c_MapMaxPredictionSpeed;

		public static ConfigEntry<float> c_SendInterval;

		public static ConfigEntry<int> c_MaxPeersPerFrame;

		public static ConfigEntry<int> c_ZDOQueueLimit;

		public static ConfigEntry<float> c_ClientInterpFactor;

		public static ConfigEntry<float> c_ClientSnapDistance;

		public static ConfigEntry<float> c_ClientMaxExtrapolation;

		public static ConfigEntry<bool> c_DiscordInfoEnabled;

		public static ConfigEntry<string> c_DiscordInfoWebhook;

		public static ConfigEntry<bool> c_DiscordErrorEnabled;

		public static ConfigEntry<string> c_DiscordErrorWebhook;

		private Harmony _harmony;

		public static VBNetTweaks Instance { get; private set; }

		private void Awake()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_0105: Expected O, but got Unknown
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			Instance = this;
			Logger = ((BaseUnityPlugin)this).Logger;
			_clientConfig = new ConfigFile(Path.Combine(Paths.ConfigPath, "VitByr/VBNetTweaks/MainConfig.cfg"), true);
			SynchronizationManager.Instance.RegisterCustomConfig(_clientConfig);
			InitClientConfigs();
			c_ModEnabled = ConfigFileExtensions.BindConfig<bool>(_clientConfig, "00 - Master", "ModEnabled", true, (c_ConfigLanguage.Value == Language.Russian) ? "Полностью включить/выключить мод VBNetTweaks" : "Completely enable/disable VBNetTweaks mod", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_TargetFPS = ConfigFileExtensions.BindConfig<int>(_clientConfig, "00 - Master", "TargetFPS", 120, (c_ConfigLanguage.Value == Language.Russian) ? "Целевой FPS для сервера (только для выделенного сервера)" : "Target FPS for server (dedicated only)", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			if (c_ModEnabled.Value)
			{
				_configSyncRPC = NetworkManager.Instance.AddRPC("VBNetTweaks_ConfigSync", new CoroutineHandler(OnAdminConfigSync), new CoroutineHandler(OnClientConfigSync));
				SynchronizationManager.Instance.AddInitialSynchronization(_configSyncRPC, (Func<ZPackage>)(() => BuildConfigPackage()));
				CreateConfigWatcher();
				if (c_DiscordInfoEnabled.Value || c_DiscordErrorEnabled.Value)
				{
					DiscordLogger.Initialize();
				}
				_harmony = new Harmony("VitByr.VBNetTweaks");
				_harmony.PatchAll(typeof(MiniMap_Patch));
				_harmony.PatchAll(typeof(NetStats_Patch));
				_harmony.PatchAll(typeof(ZDOMan_Patch));
				_harmony.PatchAll(typeof(ZNet_Patch));
				_harmony.PatchAll(typeof(ZSteamSocket_Patch));
				_harmony.PatchAll(typeof(ZSyncTransform_Patch));
				if (c_ModuleStutterGuard.Value && !Helper.IsDedicated())
				{
					ClientStutterGuard.Initialize((MonoBehaviour)(object)this);
					_harmony.PatchAll(typeof(StutterGuardTriggers));
				}
				Helper.LogDebug("Режим отладки включен");
			}
		}

		private void InitClientConfigs()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			string text = "00 - Language";
			c_ConfigLanguage = ((BaseUnityPlugin)this).Config.Bind<Language>(text, "Language", Language.Russian, new ConfigDescription("Select interface language / Выберите язык интерфейса\nRequired Restart / Требуется рестарт", (AcceptableValueBase)null, Array.Empty<object>()));
			string text2 = "01 - Debug";
			c_DebugEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "DebugEnabled", false, (c_ConfigLanguage.Value == Language.Russian) ? "Включить отладочный вывод" : "Enable debug output");
			c_VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "VerboseLogging", false, (c_ConfigLanguage.Value == Language.Russian) ? "Включить подробное логирование" : "Enable verbose logging");
			c_NetStatsLogging = ((BaseUnityPlugin)this).Config.Bind<bool>(text2, "NetStatsLogging", false, (c_ConfigLanguage.Value == Language.Russian) ? "Логирование статистики отправки/приёма пакетов (раз в интервал). Пинг становится нестабильным" : "Log send/receive packet statistics (once per interval). The ping is becoming unstable.");
			c_NetStatsInterval = ((BaseUnityPlugin)this).Config.Bind<int>(text2, "NetStatsIntervalSec", 10, (c_ConfigLanguage.Value == Language.Russian) ? "Интервал дампа статистики, сек." : "Statistics dump interval, sec.");
			string text3 = "02 - Modules";
			c_ModuleSteamOptimizations = ConfigFileExtensions.BindConfig<bool>(_clientConfig, text3, "SteamOptimizations", true, (c_ConfigLanguage.Value == Language.Russian) ? "Оптимизации Steam сокета" : "Steam socket optimizations", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ModuleZDOOptimization = ConfigFileExtensions.BindConfig<bool>(_clientConfig, text3, "ZDOOptimization", true, (c_ConfigLanguage.Value == Language.Russian) ? "Оптимизация ZDO отправок" : "Optimization ZDO sender", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ModuleMapPositionSync = ConfigFileExtensions.BindConfig<bool>(_clientConfig, text3, "MapPositionSync", true, (c_ConfigLanguage.Value == Language.Russian) ? "Включить плавные маркеры игроков на карте\nУлучшает отображение позиций игроков" : "Enable smooth player markers on map\nImproves display of player positions", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ModuleStutterGuard = ConfigFileExtensions.BindConfig<bool>(_clientConfig, text3, "StutterGuard", true, (c_ConfigLanguage.Value == Language.Russian) ? "Защита от микро-фризов (GC) на клиенте" : "Client stutter guard (GC)", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			string text4 = "03 - Steam Settings";
			c_SteamSendRateMaxKB = ConfigFileExtensions.BindConfig<int>(_clientConfig, text4, "MaxRateKB", 16384, (c_ConfigLanguage.Value == Language.Russian) ? "Максимальная скорость отправки Steam. Vanilla = ~150KB" : "Maximum Steam send rate. Vanilla = ~150KB", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_SteamSendBufferSizeKB = ConfigFileExtensions.BindConfig<int>(_clientConfig, text4, "SendBufferSizeKB", 8192, (c_ConfigLanguage.Value == Language.Russian) ? "Размер буфера отправки Steam в KB. Vanilla = ~512KB" : "Steam send buffer size in KB. Vanilla = ~512KB", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_SteamTimeoutConnected = ConfigFileExtensions.BindConfig<float>(_clientConfig, text4, "TimeoutConnected", 60000f, (c_ConfigLanguage.Value == Language.Russian) ? "Таймаут соединения Steam (миллисекунды)\nЕсли соединение неактивно дольше этого времени — оно будет разорвано\nVanilla = 30000 (30 секунд)" : "Steam connection timeout (milliseconds)\nIf connection is idle longer than this — it will be closed\nVanilla = 30000 (30 sec)", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			string text5 = "04 - ZDO Settings";
			c_SendInterval = ConfigFileExtensions.BindConfig<float>(_clientConfig, text5, "SendInterval", 0.04f, (c_ConfigLanguage.Value == Language.Russian) ? "Интервал отправки данных. Vanilla = 0.05" : "Data send interval. Vanilla = 0.05", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_MaxPeersPerFrame = ConfigFileExtensions.BindConfig<int>(_clientConfig, text5, "MaxPeersPerFrame", 0, (c_ConfigLanguage.Value == Language.Russian) ? "Макс. пиров за один кадр (0 = без лимита, бюджет сам решает)." : "Max peers per frame (0 = unlimited, budget decides).", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ZDOQueueLimit = ConfigFileExtensions.BindConfig<int>(_clientConfig, text5, "ZDOQueueLimit", 20480, (c_ConfigLanguage.Value == Language.Russian) ? "Размер буфера отправки ZDO пакетов (vanilla = 10240 байт)" : "ZDO packet send buffer size (vanilla = 10240 bytes)", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			string text6 = "05 - Map Positions";
			c_MapPositionSendInterval = ConfigFileExtensions.BindConfig<float>(_clientConfig, text6, "SendInterval", 0.75f, (c_ConfigLanguage.Value == Language.Russian) ? "Интервал отправки позиций игроков (сек)\nМеньше = плавнее, но больше трафик. Vanilla: 2.0" : "Player position send interval (sec)\nLower = smoother, but more traffic. Vanilla: 2.0", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_MapMaxPredictionSpeed = ConfigFileExtensions.BindConfig<float>(_clientConfig, text6, "MaxPredictionSpeed", 50f, (c_ConfigLanguage.Value == Language.Russian) ? "Максимальная скорость движения маркера игрока на карте (м/с).\nТелепорты (порталы, спавн) обрабатываются отдельно – маркер перепрыгивает мгновенно." : "Maximum movement speed of player marker on map (m/s).\nTeleports (portals, spawn) are handled separately – the marker jumps instantly.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			string text7 = "06 - Client Smoothing";
			c_ClientInterpFactor = ConfigFileExtensions.BindConfig<float>(_clientConfig, text7, "InterpFactor", 0.35f, (c_ConfigLanguage.Value == Language.Russian) ? "Коэффициент интерполяции для чужих объектов (vanilla 0.20).\n0.3 — отзывчивее; 0.20 — ваниль; 1.0 — без сглаживания." : "Interpolation factor for non-owned objects (vanilla 0.20).\n0.3 = snappier; 0.20 = vanilla; 1.0 = no smoothing.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ClientSnapDistance = ConfigFileExtensions.BindConfig<float>(_clientConfig, text7, "SnapDistance", 3f, (c_ConfigLanguage.Value == Language.Russian) ? "Порог мгновенного снапа вместо интерполяции (vanilla 5.0). Меньше — раньше снап, меньше визуального разъезда." : "Distance threshold for instant snap instead of interpolation (vanilla 5.0). Lower = earlier snap, less visual desync.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_ClientMaxExtrapolation = ConfigFileExtensions.BindConfig<float>(_clientConfig, text7, "MaxExtrapolationSeconds", 0.5f, (c_ConfigLanguage.Value == Language.Russian) ? "Лимит экстраполяции чужих объектов при пропаже апдейтов (vanilla 2.0).\n0.5 — мобы не «бегут в стену» при потере пакетов." : "Cap on extrapolation of non-owned objects when updates are missing (vanilla 2.0).\n0.5 = mobs stop running into walls on packet loss.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			string text8 = "07 - Discord Webhooks";
			c_DiscordInfoEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, text8, "InfoEnabled", false, "Enable info notifications batching / Включить батчинг уведомлений", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_DiscordInfoWebhook = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, text8, "InfoWebhookUrl", "", "Discord webhook URL for info messages / URL вебхука для уведомлений", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_DiscordErrorEnabled = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, text8, "ErrorEnabled", false, "Enable error reporting with network context / Включить отчеты об ошибках", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			c_DiscordErrorWebhook = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, text8, "ErrorWebhookUrl", "", "Discord webhook URL for errors / URL вебхука для ошибок", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		}

		public ZPackage BuildConfigPackage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			try
			{
				val.Write(c_ModEnabled.Value);
				val.Write(c_ModuleSteamOptimizations.Value);
				val.Write(c_ModuleZDOOptimization.Value);
				val.Write(c_ModuleMapPositionSync.Value);
				val.Write(c_SteamSendRateMaxKB.Value);
				val.Write(c_SteamSendBufferSizeKB.Value);
				val.Write(c_SteamTimeoutConnected.Value);
				val.Write(c_MapPositionSendInterval.Value);
				val.Write(c_MapMaxPredictionSpeed.Value);
				val.Write(c_SendInterval.Value);
				val.Write(c_MaxPeersPerFrame.Value);
				val.Write(c_ZDOQueueLimit.Value);
			}
			catch (Exception ex)
			{
				Helper.LogDebug("Error building config package: " + ex.Message);
				return new ZPackage();
			}
			return val;
		}

		private void ApplyConfigFromPackage(ZPackage pkg)
		{
			if (pkg == null || pkg.GetArray().Length == 0)
			{
				Helper.LogDebug("Received empty config package");
				return;
			}
			try
			{
				pkg.SetPos(0);
				c_ModEnabled.Value = pkg.ReadBool();
				c_ModuleSteamOptimizations.Value = pkg.ReadBool();
				c_ModuleZDOOptimization.Value = pkg.ReadBool();
				c_ModuleMapPositionSync.Value = pkg.ReadBool();
				c_SteamSendRateMaxKB.Value = pkg.ReadInt();
				c_SteamSendBufferSizeKB.Value = pkg.ReadInt();
				c_SteamTimeoutConnected.Value = pkg.ReadSingle();
				c_MapPositionSendInterval.Value = pkg.ReadSingle();
				c_MapMaxPredictionSpeed.Value = pkg.ReadSingle();
				c_SendInterval.Value = pkg.ReadSingle();
				c_MaxPeersPerFrame.Value = pkg.ReadInt();
				c_ZDOQueueLimit.Value = pkg.ReadInt();
			}
			catch (Exception ex)
			{
				Helper.LogDebug("Error applying config package: " + ex.Message);
			}
		}

		private IEnumerator OnAdminConfigSync(long sender, ZPackage pkg)
		{
			if (!Helper.IsDedicated())
			{
				yield break;
			}
			ZPackage serverConfigPkg = BuildConfigPackage();
			byte[] data = serverConfigPkg.GetArray();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				ZPackage copyPkg = new ZPackage(data);
				_configSyncRPC.SendPackage(new List<ZNetPeer> { peer }, copyPkg);
			}
			Helper.LogDebug("Server config broadcast to all clients");
		}

		public IEnumerator OnClientConfigSync(long sender, ZPackage pkg)
		{
			Helper.LogDebug($"Клиент получил конфиг от сервера {sender}");
			ApplyConfigFromPackage(pkg);
			_clientConfig.SaveOnConfigSet = true;
			_clientConfig.Save();
			yield break;
		}

		private void CreateConfigWatcher()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			ConfigFileWatcher val = new ConfigFileWatcher(_clientConfig, 1000L);
			val.OnConfigFileReloaded += delegate
			{
				if (Helper.IsDedicated())
				{
					Helper.LogDebug("Server config changed, broadcasting to all clients");
					((MonoBehaviour)this).StartCoroutine(ApplyServerConfigChanges());
				}
			};
		}

		public IEnumerator ApplyServerConfigChanges()
		{
			yield return null;
			ZPackage pkg = BuildConfigPackage();
			if (pkg.GetArray().Length == 0)
			{
				yield break;
			}
			byte[] data = pkg.GetArray();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				ZPackage copyPkg = new ZPackage(data);
				_configSyncRPC.SendPackage(new List<ZNetPeer> { peer }, copyPkg);
			}
			Helper.LogDebug("Server config broadcast to all clients");
		}

		private void OnDestroy()
		{
			ConfigFile clientConfig = _clientConfig;
			if (clientConfig != null)
			{
				clientConfig.Save();
			}
			ClientStutterGuard.Shutdown();
			DiscordLogger.Shutdown();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace VBNetTweaks.Utils
{
	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;
		}

		private struct MemoryPressureSnapshot
		{
			public bool Known;

			public ulong TotalMB;

			public ulong AvailableMB;

			public int LoadPercent;
		}

		private static MonoBehaviour _host;

		private static Coroutine _cleanupCoroutine;

		private static volatile int _mainThreadId;

		private static double _criticalUntil;

		private static bool _pendingGc;

		private static double _firstPendingSince;

		private static bool _runningCleanup;

		private const float TeleportWindow = 5f;

		private const float CombatWindow = 2f;

		private const float FullSnapshotWindow = 1.5f;

		private const float MaxDelaySeconds = 30f;

		private const int MinFreeMemoryMB = 2048;

		private const int MemoryPressureThresholdPercent = 75;

		private const float IdleCleanupPollSeconds = 1f;

		internal static bool IsActive => VBNetTweaks.c_ModuleStutterGuard.Value && !Helper.IsDedicated();

		internal static void Initialize(MonoBehaviour host)
		{
			_host = host;
			_mainThreadId = Thread.CurrentThread.ManagedThreadId;
			_criticalUntil = 0.0;
			_pendingGc = false;
			_firstPendingSince = 0.0;
			_runningCleanup = false;
			EnsureCleanupScheduler();
		}

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

		internal static void MarkTeleportWindow()
		{
			ExtendCriticalWindow(5f);
		}

		internal static void MarkCombatWindow()
		{
			ExtendCriticalWindow(2f);
		}

		internal static void MarkFullSnapshotWindow()
		{
			ExtendCriticalWindow(1.5f);
		}

		private static void ExtendCriticalWindow(float seconds)
		{
			if (IsActive && !(seconds <= 0f))
			{
				double num = Time.realtimeSinceStartupAsDouble + (double)seconds;
				if (num > _criticalUntil)
				{
					_criticalUntil = num;
				}
			}
		}

		internal static bool TryHandleGcCollect()
		{
			if (Thread.CurrentThread.ManagedThreadId != _mainThreadId)
			{
				return true;
			}
			if (_runningCleanup)
			{
				return true;
			}
			if (!IsActive)
			{
				ClearPendingCleanup();
				return true;
			}
			if (ShouldDelayCleanup())
			{
				RequestPending();
				return false;
			}
			ClearPendingCleanup();
			return true;
		}

		private static void RequestPending()
		{
			_pendingGc = true;
			if (_firstPendingSince <= 0.0)
			{
				_firstPendingSince = Time.realtimeSinceStartupAsDouble;
			}
			EnsureCleanupScheduler();
		}

		private static void EnsureCleanupScheduler()
		{
			if (_cleanupCoroutine == null && Object.op_Implicit((Object)(object)_host) && IsActive)
			{
				_cleanupCoroutine = _host.StartCoroutine(CleanupScheduler());
			}
		}

		private static IEnumerator CleanupScheduler()
		{
			while (true)
			{
				yield return (object)new WaitForSecondsRealtime(Math.Max(0.25f, 1f));
				TryRunPendingCleanup();
			}
		}

		private static bool TryRunPendingCleanup()
		{
			if (!_pendingGc || _runningCleanup)
			{
				return false;
			}
			if (ShouldDelayCleanup())
			{
				return false;
			}
			return RunPendingCleanup();
		}

		private static bool RunPendingCleanup()
		{
			if (!_pendingGc || _runningCleanup)
			{
				return false;
			}
			ClearPendingCleanup();
			_runningCleanup = true;
			try
			{
				GC.Collect();
			}
			finally
			{
				_runningCleanup = false;
			}
			return true;
		}

		private static void ClearPendingCleanup()
		{
			_pendingGc = false;
			_firstPendingSince = 0.0;
		}

		private static bool ShouldDelayCleanup()
		{
			if (!IsActive)
			{
				return false;
			}
			if (_firstPendingSince > 0.0 && Time.realtimeSinceStartupAsDouble - _firstPendingSince >= 30.0)
			{
				return false;
			}
			if (_criticalUntil > Time.realtimeSinceStartupAsDouble)
			{
				return !IsMemoryPressure();
			}
			return IsMemoryPlentiful();
		}

		private static bool IsMemoryPlentiful()
		{
			MemoryPressureSnapshot memorySnapshot = GetMemorySnapshot();
			if (!memorySnapshot.Known)
			{
				return false;
			}
			return memorySnapshot.LoadPercent < 75 && memorySnapshot.AvailableMB >= 2048;
		}

		private static bool IsMemoryPressure()
		{
			MemoryPressureSnapshot memorySnapshot = GetMemorySnapshot();
			if (!memorySnapshot.Known)
			{
				return false;
			}
			if (memorySnapshot.LoadPercent >= 75)
			{
				return true;
			}
			return memorySnapshot.AvailableMB < 2048;
		}

		private static MemoryPressureSnapshot GetMemorySnapshot()
		{
			if (TryGetWindowsMemory(out var snapshot))
			{
				return snapshot;
			}
			if (TryGetLinuxMemory(out var snapshot2))
			{
				return snapshot2;
			}
			return new MemoryPressureSnapshot
			{
				Known = false
			};
		}

		private static bool TryGetLinuxMemory(out MemoryPressureSnapshot snapshot)
		{
			snapshot = default(MemoryPressureSnapshot);
			try
			{
				if (!File.Exists("/proc/meminfo"))
				{
					return false;
				}
				ulong num = 0uL;
				ulong num2 = 0uL;
				bool flag = false;
				bool flag2 = false;
				string[] array = File.ReadAllLines("/proc/meminfo");
				string[] array2 = array;
				foreach (string text in array2)
				{
					if (text.StartsWith("MemTotal:", StringComparison.Ordinal))
					{
						num = ParseMemInfoValue(text);
						flag = true;
					}
					else if (text.StartsWith("MemAvailable:", StringComparison.Ordinal))
					{
						num2 = ParseMemInfoValue(text);
						flag2 = true;
					}
					if (flag && flag2)
					{
						break;
					}
				}
				if (!flag || !flag2 || num == 0)
				{
					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 ParseMemInfoValue(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;
		}

		private static bool TryGetWindowsMemory(out MemoryPressureSnapshot snapshot)
		{
			snapshot = default(MemoryPressureSnapshot);
			if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				return false;
			}
			try
			{
				MEMORYSTATUSEX lpBuffer = new MEMORYSTATUSEX
				{
					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;
			}
		}

		[DllImport("kernel32.dll", SetLastError = true)]
		private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
	}
	internal static class DiscordLogger
	{
		private static readonly HttpClient _httpClient = new HttpClient
		{
			Timeout = TimeSpan.FromSeconds(5.0)
		};

		private static readonly ConcurrentQueue<string> _infoQueue = new ConcurrentQueue<string>();

		private static readonly ConcurrentQueue<string> _errorQueue = new ConcurrentQueue<string>();

		private static CancellationTokenSource _cts;

		private static Task _workerTask;

		private const int MaxBatchChars = 1800;

		private const float InfoFlushInterval = 5f;

		internal static void Initialize()
		{
			if (_workerTask == null || _workerTask.IsCompleted)
			{
				_cts = new CancellationTokenSource();
				_workerTask = Task.Run((Func<Task?>)WorkerLoop);
				Helper.LogDebug("[Discord] Logger initialized");
			}
		}

		internal static void Shutdown()
		{
			try
			{
				_cts?.Cancel();
				_workerTask?.Wait(TimeSpan.FromSeconds(2.0));
			}
			catch
			{
			}
			finally
			{
				_cts?.Dispose();
				_cts = null;
				_workerTask = null;
			}
		}

		internal static void SendInfo(string message, string sender = null)
		{
			if (VBNetTweaks.c_DiscordInfoEnabled.Value && !string.IsNullOrEmpty(VBNetTweaks.c_DiscordInfoWebhook.Value))
			{
				string item = FormatMessage(message, sender, isError: false);
				_infoQueue.Enqueue(item);
			}
		}

		internal static void SendError(string message, string sender = null, Exception ex = null)
		{
			if (VBNetTweaks.c_DiscordErrorEnabled.Value && !string.IsNullOrEmpty(VBNetTweaks.c_DiscordErrorWebhook.Value))
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("\ud83d\udd34 **ERROR** | " + GetTimestamp());
				stringBuilder.AppendLine("**From:** " + (sender ?? GetDefaultSender()));
				stringBuilder.AppendLine("**Message:** " + message);
				if (ex != null)
				{
					stringBuilder.AppendLine("**Exception:** `" + ex.GetType().Name + "`");
					stringBuilder.AppendLine("```" + Truncate(ex.Message, 300) + "```");
				}
				AppendNetworkContext(stringBuilder);
				string text = stringBuilder.ToString();
				if (text.Length > 1900)
				{
					text = text.Substring(0, 1900) + "...";
				}
				_errorQueue.Enqueue(text);
			}
		}

		private static async Task WorkerLoop()
		{
			StringBuilder infoBatch = new StringBuilder();
			float lastInfoFlush = Time.realtimeSinceStartup;
			while (!_cts.Token.IsCancellationRequested)
			{
				try
				{
					await Task.Delay(500, _cts.Token);
				}
				catch (OperationCanceledException)
				{
					break;
				}
				string errorMsg;
				while (_errorQueue.TryDequeue(out errorMsg))
				{
					await PostToWebhook(VBNetTweaks.c_DiscordErrorWebhook.Value, errorMsg);
				}
				bool shouldFlush = Time.realtimeSinceStartup - lastInfoFlush >= 5f;
				string infoMsg;
				while (_infoQueue.TryDequeue(out infoMsg))
				{
					if (infoBatch.Length + infoMsg.Length + 1 > 1800)
					{
						await PostToWebhook(VBNetTweaks.c_DiscordInfoWebhook.Value, infoBatch.ToString());
						infoBatch.Clear();
					}
					if (infoBatch.Length > 0)
					{
						infoBatch.Append('\n');
					}
					infoBatch.Append(infoMsg);
				}
				if (shouldFlush && infoBatch.Length > 0)
				{
					await PostToWebhook(VBNetTweaks.c_DiscordInfoWebhook.Value, infoBatch.ToString());
					infoBatch.Clear();
					lastInfoFlush = Time.realtimeSinceStartup;
				}
			}
			if (infoBatch.Length > 0 && !string.IsNullOrEmpty(VBNetTweaks.c_DiscordInfoWebhook.Value))
			{
				await PostToWebhook(VBNetTweaks.c_DiscordInfoWebhook.Value, infoBatch.ToString());
			}
		}

		private static async Task PostToWebhook(string url, string content)
		{
			if (string.IsNullOrEmpty(url) || string.IsNullOrEmpty(content))
			{
				return;
			}
			try
			{
				string payload = "{\"content\":" + EscapeJson(content) + "}";
				HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, url)
				{
					Content = (HttpContent)new StringContent(payload, Encoding.UTF8, "application/json")
				};
				try
				{
					HttpResponseMessage response = await ((HttpMessageInvoker)_httpClient).SendAsync(request, _cts.Token);
					try
					{
						if (!response.IsSuccessStatusCode && VBNetTweaks.c_DebugEnabled.Value)
						{
							Helper.LogDebug($"[Discord] Webhook failed: {(int)response.StatusCode}");
						}
					}
					finally
					{
						((IDisposable)response)?.Dispose();
					}
				}
				finally
				{
					((IDisposable)request)?.Dispose();
				}
			}
			catch (OperationCanceledException)
			{
			}
			catch (Exception ex2)
			{
				if (VBNetTweaks.c_DebugEnabled.Value)
				{
					Helper.LogDebug("[Discord] Post exception: " + ex2.Message);
				}
			}
		}

		private static string FormatMessage(string message, string sender, bool isError)
		{
			string text = (isError ? "\ud83d\udd34" : "ℹ\ufe0f");
			return text + " [" + GetTimestamp() + "] **" + (sender ?? GetDefaultSender()) + "**: " + message;
		}

		private static string GetDefaultSender()
		{
			if (Helper.IsServer())
			{
				return "Server";
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				return Player.m_localPlayer.GetPlayerName();
			}
			return "Client";
		}

		private static string GetTimestamp()
		{
			return DateTime.Now.ToString("HH:mm:ss");
		}

		private static void AppendNetworkContext(StringBuilder sb)
		{
			try
			{
				if (!((Object)(object)ZNet.instance == (Object)null))
				{
					sb.AppendLine("**\ud83d\udcca Network Context:**");
					sb.AppendLine($"• Peers: {ZNet.instance.GetPeers().Count}");
					ZDOMan instance = ZDOMan.instance;
					sb.AppendLine($"• ZDO Sent/s: {((instance != null) ? instance.GetSentZDOs() : 0)}");
					ZDOMan instance2 = ZDOMan.instance;
					sb.AppendLine($"• ZDO Recv/s: {((instance2 != null) ? instance2.GetRecvZDOs() : 0)}");
					sb.AppendLine($"• Deferred: {NetStats.ZdoDeferred}");
					sb.AppendLine($"• SkipQueueFull: {NetStats.SkipQueueFull}");
					sb.AppendLine($"• SkipLowSpace: {NetStats.SkipLowSpace}");
					sb.AppendLine($"• SteamSendFail: {NetStats.SteamSendFail}");
					sb.AppendLine($"• UnknownHost: {NetStats.ZdoUnknownHost}");
				}
			}
			catch
			{
			}
		}

		private static string Truncate(string value, int maxLen)
		{
			if (string.IsNullOrEmpty(value))
			{
				return "";
			}
			return (value.Length <= maxLen) ? value : (value.Substring(0, maxLen) + "...");
		}

		private static string EscapeJson(string s)
		{
			if (s == null)
			{
				return "null";
			}
			StringBuilder stringBuilder = new StringBuilder("\"");
			foreach (char c in s)
			{
				switch (c)
				{
				case '"':
					stringBuilder.Append("\\\"");
					break;
				case '\\':
					stringBuilder.Append("\\\\");
					break;
				case '\n':
					stringBuilder.Append("\\n");
					break;
				case '\r':
					stringBuilder.Append("\\r");
					break;
				case '\t':
					stringBuilder.Append("\\t");
					break;
				default:
					stringBuilder.Append(c);
					break;
				}
			}
			stringBuilder.Append('"');
			return stringBuilder.ToString();
		}
	}
	public enum Language
	{
		Russian,
		English
	}
	public static class Helper
	{
		public static readonly Dictionary<ZRpc, List<ZPackage>> _buffers = new Dictionary<ZRpc, List<ZPackage>>();

		public static float GetTimeoutConnected()
		{
			return Mathf.Clamp(VBNetTweaks.c_SteamTimeoutConnected.Value, 5000f, 600000f);
		}

		public static int GetSteamSendBufferSizeKB()
		{
			return Math.Max(524288, VBNetTweaks.c_SteamSendBufferSizeKB.Value * 1024);
		}

		public static int GetSteamSendRateMaxKB()
		{
			return Math.Max(131072, VBNetTweaks.c_SteamSendRateMaxKB.Value * 1024);
		}

		public static int GetQueueLimit()
		{
			if (!VBNetTweaks.c_ModuleZDOOptimization.Value)
			{
				return 10240;
			}
			int num = Mathf.Max(10240, VBNetTweaks.c_ZDOQueueLimit.Value);
			int num2 = (Object.op_Implicit((Object)(object)ZNet.instance) ? ZNet.instance.GetPeers().Count : 0);
			float num3 = 1f + Mathf.Min(2f, (float)num2 / 10f);
			int num4 = Mathf.RoundToInt((float)num * num3);
			return Mathf.Clamp(num4, 10240, 196608);
		}

		public static float GetSendInterval()
		{
			return Mathf.Clamp(VBNetTweaks.c_SendInterval.Value, 0.01f, 0.2f);
		}

		public static float GetMapPositionSendInterval()
		{
			return Mathf.Clamp(VBNetTweaks.c_MapPositionSendInterval.Value, 0.1f, 2f);
		}

		public static bool IsDedicated()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			return (int)SystemInfo.graphicsDeviceType == 4;
		}

		public static bool IsServer()
		{
			return Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer();
		}

		public static void LogDebug(string message)
		{
			if (VBNetTweaks.c_DebugEnabled.Value)
			{
				VBNetTweaks.Logger.LogWarning((object)message);
			}
		}

		public static void LogVerbose(string message)
		{
			if (VBNetTweaks.c_VerboseLogging.Value)
			{
				VBNetTweaks.Logger.LogInfo((object)message);
			}
		}
	}
	public static class NetStats
	{
		private struct Snapshot
		{
			public float Seconds;

			public long SendOkPackets;

			public long SendEmpty;

			public long SkipQueueFull;

			public long SkipLowSpace;

			public long ZdoSerialized;

			public long ZdoDeferred;

			public long SteamSendFail;

			public long ZdoDataIn;

			public long ZdoApplied;

			public long ZdoUnknownHost;

			public long PeersServiced;

			public bool IsServer;

			public string SenderName;

			public float ServerFps;

			public int TotalZdoCount;

			public int ActiveSectors;

			public int PeerCount;

			public float LocalQuality;

			public float RemoteQuality;

			public int Ping;

			public float OutBytesSec;

			public float InBytesSec;

			public float JitterMs;
		}

		public static long SendOkPackets;

		public static long SendEmpty;

		public static long SkipQueueFull;

		public static long SkipLowSpace;

		public static long ZdoSerialized;

		public static long ZdoDeferred;

		public static long SteamSendFail;

		public static long ZdoDataIn;

		public static long ZdoApplied;

		public static long ZdoUnknownHost;

		public static long PeersServiced;

		private static float _timer;

		private static volatile bool _isPrinting;

		private static readonly float[] _pingHistory = new float[20];

		private static int _pingIdx;

		public static bool Enabled => VBNetTweaks.c_NetStatsLogging.Value;

		public static void ResetCounters()
		{
			SendOkPackets = (SendEmpty = (SkipQueueFull = (SkipLowSpace = 0L)));
			ZdoSerialized = (ZdoDeferred = (SteamSendFail = 0L));
			ZdoDataIn = (ZdoApplied = (ZdoUnknownHost = 0L));
			PeersServiced = 0L;
		}

		public static void Tick(float dt)
		{
			if (!Enabled || _isPrinting)
			{
				return;
			}
			_timer += dt;
			if (_timer < (float)VBNetTweaks.c_NetStatsInterval.Value)
			{
				return;
			}
			float timer = _timer;
			_timer = 0f;
			bool flag = Helper.IsServer();
			string senderName = (flag ? "Server" : (((Object)(object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerName() : "Client"));
			Snapshot snapshot = new Snapshot
			{
				Seconds = timer,
				SendOkPackets = SendOkPackets,
				SendEmpty = SendEmpty,
				SkipQueueFull = SkipQueueFull,
				SkipLowSpace = SkipLowSpace,
				ZdoSerialized = ZdoSerialized,
				ZdoDeferred = ZdoDeferred,
				SteamSendFail = SteamSendFail,
				ZdoDataIn = ZdoDataIn,
				ZdoApplied = ZdoApplied,
				ZdoUnknownHost = ZdoUnknownHost,
				PeersServiced = PeersServiced,
				IsServer = flag,
				SenderName = senderName
			};
			if (flag)
			{
				snapshot.ServerFps = 1f / Mathf.Max(Time.unscaledDeltaTime, 0.001f);
				ref Snapshot reference = ref snapshot;
				ZDOMan instance = ZDOMan.instance;
				reference.TotalZdoCount = ((instance != null) ? instance.NrOfObjects() : 0);
				int num = 0;
				if (ZDOMan.instance?.m_objectsBySector != null)
				{
					List<ZDO>[] objectsBySector = ZDOMan.instance.m_objectsBySector;
					foreach (List<ZDO> list in objectsBySector)
					{
						if (list != null && list.Count > 0)
						{
							num++;
						}
					}
				}
				snapshot.ActiveSectors = num;
				ref Snapshot reference2 = ref snapshot;
				ZNet instance2 = ZNet.instance;
				reference2.PeerCount = ((instance2 == null) ? ((int?)null) : instance2.GetPeers()?.Count).GetValueOrDefault();
			}
			else if ((Object)(object)ZNet.instance != (Object)null)
			{
				ZNet.instance.GetNetStats(ref snapshot.LocalQuality, ref snapshot.RemoteQuality, ref snapshot.Ping, ref snapshot.OutBytesSec, ref snapshot.InBytesSec);
				snapshot.PeerCount = ZNet.instance.GetPeers()?.Count ?? 0;
				if (snapshot.Ping > 0)
				{
					_pingHistory[_pingIdx % _pingHistory.Length] = snapshot.Ping;
					_pingIdx++;
					float num2 = float.MaxValue;
					float num3 = float.MinValue;
					int num4 = Mathf.Min(_pingIdx, _pingHistory.Length);
					for (int j = 0; j < num4; j++)
					{
						if (_pingHistory[j] < num2)
						{
							num2 = _pingHistory[j];
						}
						if (_pingHistory[j] > num3)
						{
							num3 = _pingHistory[j];
						}
					}
					snapshot.JitterMs = num3 - num2;
				}
			}
			ResetCounters();
			_isPrinting = true;
			Task.Run(delegate
			{
				try
				{
					PrintFromSnapshot(snapshot);
				}
				finally
				{
					_isPrinting = false;
				}
			});
		}

		private static void PrintFromSnapshot(Snapshot snap)
		{
			bool flag = snap.ZdoDeferred > 5000;
			StringBuilder stringBuilder = new StringBuilder(512);
			string arg = (snap.IsServer ? "[SRV]" : "[CLI]");
			float num = (float)snap.ZdoSerialized / snap.Seconds;
			float num2 = (float)snap.ZdoDeferred / snap.Seconds;
			string arg2 = "✅";
			if (snap.SteamSendFail > 0 || snap.SkipQueueFull > 0)
			{
				arg2 = "\ud83d\udd34";
			}
			else if (snap.IsServer && snap.ServerFps < 20f)
			{
				arg2 = "\ud83d\udd34";
			}
			else if (snap.ZdoDeferred > 100 || snap.SkipLowSpace > 0)
			{
				arg2 = "⚠\ufe0f";
			}
			else if (snap.IsServer && snap.ServerFps < 25f)
			{
				arg2 = "⚠\ufe0f";
			}
			stringBuilder.AppendLine($"{arg} {arg2} NetStats ({snap.Seconds:F1}s)");
			stringBuilder.AppendLine($"  OUT: {num:F0} zdo/s | pk={snap.SendOkPackets} | deferred={snap.ZdoDeferred} ({num2:F0}/s) | skipQ={snap.SkipQueueFull} skipL={snap.SkipLowSpace} | fail={snap.SteamSendFail}");
			stringBuilder.AppendLine($"  IN:  {(float)snap.ZdoApplied / snap.Seconds:F0} zdo/s | pk={snap.ZdoDataIn} | unknown={snap.ZdoUnknownHost} | svc={snap.PeersServiced}");
			if (snap.IsServer)
			{
				stringBuilder.AppendLine($"  SYS: fps={snap.ServerFps:F0} | zdos={snap.TotalZdoCount} | sectors={snap.ActiveSectors} | peers={snap.PeerCount}");
			}
			else
			{
				string text = ((snap.JitterMs > 0f) ? $" | jitter={snap.JitterMs:F0}ms" : "");
				stringBuilder.AppendLine($"  NET: ping={snap.Ping}ms{text} | ↑{snap.OutBytesSec / 1024f:F1} KB/s ↓{snap.InBytesSec / 1024f:F1} KB/s | peers={snap.PeerCount} | quality L:{snap.LocalQuality:P0} R:{snap.RemoteQuality:P0}");
			}
			VBNetTweaks.Logger.LogWarning((object)stringBuilder.ToString());
			if (!flag)
			{
				DiscordLogger.SendInfo(stringBuilder.ToString(), snap.SenderName);
			}
			if (!flag && (snap.SkipQueueFull > 50 || snap.SteamSendFail > 10 || snap.ZdoDeferred > 10000 || (snap.IsServer && snap.ServerFps < 15f)))
			{
				string text2 = (snap.IsServer ? $", FPS={snap.ServerFps:F0}" : "");
				DiscordLogger.SendError($"Network degradation! QueueFull={snap.SkipQueueFull}, SteamFail={snap.SteamSendFail}, Deferred={snap.ZdoDeferred}{text2}", snap.SenderName);
			}
		}
	}
	public static class SyncTuning
	{
		private static int _frame;

		private static float _interpFactor;

		private static float _snapDistance;

		private static float _maxExtrapolation;

		private static void Refresh()
		{
			int frameCount = Time.frameCount;
			if (frameCount != _frame)
			{
				_frame = frameCount;
				bool flag = Helper.IsDedicated();
				_interpFactor = (flag ? 0.2f : Mathf.Clamp(VBNetTweaks.c_ClientInterpFactor.Value, 0.05f, 1f));
				_snapDistance = (flag ? 5f : Mathf.Clamp(VBNetTweaks.c_ClientSnapDistance.Value, 1f, 20f));
				_maxExtrapolation = (flag ? 2f : Mathf.Clamp(VBNetTweaks.c_ClientMaxExtrapolation.Value, 0.1f, 2f));
			}
		}

		public static float GetInterpFactor()
		{
			Refresh();
			return _interpFactor;
		}

		public static float GetSnapDistance()
		{
			Refresh();
			return _snapDistance;
		}

		public static float GetMaxExtrapolationSeconds()
		{
			Refresh();
			return _maxExtrapolation;
		}
	}
}
namespace VBNetTweaks.Patches
{
	[HarmonyPatch]
	public static class MiniMap_Patch
	{
		public class PlayerTrackData
		{
			public Vector3 lastRealPosition;

			public Vector3 lastRealVelocity;

			public Vector3 smoothPosition;

			public Vector3 smoothVelocity;

			public float lastUpdateTime;

			public float teleportEndTime;
		}

		public static readonly Dictionary<long, PlayerTrackData> _playerTracks = new Dictionary<long, PlayerTrackData>();

		private static int _lastCleanupFrame;

		[HarmonyPatch(typeof(Minimap), "UpdatePlayerPins")]
		[HarmonyPostfix]
		private static void UpdatePlayerPins_SmoothPostfix(Minimap __instance, float dt)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02af: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_031b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0305: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Unknown result type (might be due to invalid IL or missing references)
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0354: Unknown result type (might be due to invalid IL or missing references)
			//IL_0359: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0374: Unknown result type (might be due to invalid IL or missing references)
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			if (!VBNetTweaks.c_ModuleMapPositionSync.Value || Helper.IsDedicated() || __instance.m_playerPins == null || __instance.m_tempPlayerInfo == null)
			{
				return;
			}
			int num = Mathf.Min(__instance.m_playerPins.Count, __instance.m_tempPlayerInfo.Count);
			if (num == 0)
			{
				return;
			}
			float time = Time.time;
			float value = VBNetTweaks.c_MapMaxPredictionSpeed.Value;
			if (Time.frameCount - _lastCleanupFrame > 600)
			{
				_lastCleanupFrame = Time.frameCount;
				CleanupStaleTracks(time);
			}
			for (int i = 0; i < num; i++)
			{
				PinData val = __instance.m_playerPins[i];
				PlayerInfo val2 = __instance.m_tempPlayerInfo[i];
				if (!val2.m_publicPosition)
				{
					continue;
				}
				long playerId = GetPlayerId(val2);
				if (playerId == 0)
				{
					continue;
				}
				Vector3 position = val2.m_position;
				if (!_playerTracks.TryGetValue(playerId, out var value2))
				{
					value2 = new PlayerTrackData();
					_playerTracks[playerId] = value2;
					value2.smoothPosition = position;
					value2.lastRealPosition = position;
					value2.lastUpdateTime = time;
					val.m_pos = position;
					continue;
				}
				float num2 = time - value2.lastUpdateTime;
				if (num2 > 0.001f)
				{
					Vector3 val3 = position - value2.lastRealPosition;
					float magnitude = ((Vector3)(ref val3)).magnitude;
					if (magnitude > 50f)
					{
						value2.teleportEndTime = time + 0.3f;
						value2.lastRealVelocity = Vector3.zero;
					}
					else if (num2 < 2f && magnitude > 0.01f)
					{
						value2.lastRealVelocity = val3 / num2;
						if (((Vector3)(ref value2.lastRealVelocity)).magnitude > 100f)
						{
							value2.lastRealVelocity = ((Vector3)(ref value2.lastRealVelocity)).normalized * 100f;
						}
					}
				}
				if (num2 >= 2f)
				{
					float num3 = Vector3.Distance(value2.smoothPosition, position);
					if (num3 > 50f)
					{
						value2.teleportEndTime = time + 0.3f;
						value2.lastRealVelocity = Vector3.zero;
					}
				}
				value2.lastRealPosition = position;
				value2.lastUpdateTime = time;
				if (value2.teleportEndTime > time)
				{
					val.m_pos = position;
					value2.smoothPosition = position;
					value2.smoothVelocity = Vector3.zero;
					continue;
				}
				Vector3 val4 = position;
				float num4 = time - value2.lastUpdateTime;
				if (num4 > 0.05f && ((Vector3)(ref value2.lastRealVelocity)).magnitude > 0.5f)
				{
					float num5 = Mathf.Min(num4, 0.5f);
					val4 = position + value2.lastRealVelocity * num5;
				}
				float num6 = Vector3.Distance(value2.smoothPosition, val4);
				float num7 = ((num6 > 30f) ? 0.01f : ((num6 > 10f) ? 0.03f : 0.05f));
				value2.smoothPosition = Vector3.SmoothDamp(value2.smoothPosition, val4, ref value2.smoothVelocity, num7, value, dt);
				val.m_pos = value2.smoothPosition;
			}
		}

		private static long GetPlayerId(PlayerInfo info)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!((ZDOID)(ref info.m_characterID)).IsNone())
			{
				return ((ZDOID)(ref info.m_characterID)).UserID;
			}
			if (!string.IsNullOrEmpty(info.m_name))
			{
				return info.m_name.GetHashCode();
			}
			return 0L;
		}

		private static void CleanupStaleTracks(float now)
		{
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, PlayerTrackData> playerTrack in _playerTracks)
			{
				if (now - playerTrack.Value.lastUpdateTime > 10f)
				{
					list.Add(playerTrack.Key);
				}
			}
			foreach (long item in list)
			{
				_playerTracks.Remove(item);
			}
		}
	}
	[HarmonyPatch]
	public static class NetStats_Patch
	{
		public struct SendState
		{
			public bool active;

			public int preSent;

			public int skip;
		}

		[HarmonyPatch(typeof(ZNet), "Update")]
		[HarmonyPostfix]
		private static void ZNetUpdate_Postfix()
		{
			NetStats.Tick(Time.deltaTime);
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyPrefix]
		private static void SendZDOs_Prefix(ZDOMan __instance, ZDOPeer peer, bool flush, ref SendState __state)
		{
			__state = default(SendState);
			if (NetStats.Enabled && peer?.m_peer?.m_socket != null)
			{
				__state.active = true;
				__state.preSent = __instance.m_zdosSent;
				int sendQueueSize = peer.m_peer.m_socket.GetSendQueueSize();
				int queueLimit = Helper.GetQueueLimit();
				if (!flush && sendQueueSize > queueLimit)
				{
					__state.skip = 1;
					NetStats.SkipQueueFull++;
				}
				else if (queueLimit - sendQueueSize < 2048)
				{
					__state.skip = 2;
					NetStats.SkipLowSpace++;
				}
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyPostfix]
		private static void SendZDOs_Postfix(ZDOMan __instance, bool __result, SendState __state)
		{
			if (!__state.active || __state.skip != 0)
			{
				return;
			}
			if (__result)
			{
				NetStats.SendOkPackets++;
				int num = __instance.m_zdosSent - __state.preSent;
				if (num > 0)
				{
					NetStats.ZdoSerialized += num;
				}
				int num2 = __instance.m_tempToSync.Count - num;
				if (num2 > 0)
				{
					NetStats.ZdoDeferred += num2;
				}
			}
			else
			{
				NetStats.SendEmpty++;
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")]
		[HarmonyPrefix]
		private static void RpcZdoData_Prefix(ZDOMan __instance, ZRpc rpc, ref int __state)
		{
			__state = -1;
			if (NetStats.Enabled)
			{
				if (__instance.FindPeer(rpc) == null)
				{
					NetStats.ZdoUnknownHost++;
				}
				else
				{
					__state = __instance.m_zdosRecv;
				}
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")]
		[HarmonyPostfix]
		private static void RpcZdoData_Postfix(ZDOMan __instance, int __state)
		{
			if (__state >= 0)
			{
				NetStats.ZdoDataIn++;
				int num = __instance.m_zdosRecv - __state;
				if (num > 0)
				{
					NetStats.ZdoApplied += num;
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class StutterGuardTriggers
	{
		[HarmonyPatch(typeof(ZNetScene), "InLoadingScreen")]
		[HarmonyPostfix]
		private static void LoadingScreen_Postfix(bool __result)
		{
			if (__result)
			{
				ClientStutterGuard.MarkTeleportWindow();
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")]
		[HarmonyPrefix]
		private static void ZdoData_Prefix(object[] __args)
		{
			if (Helper.IsServer() || __args == null)
			{
				return;
			}
			foreach (object obj in __args)
			{
				ZPackage val = (ZPackage)((obj is ZPackage) ? obj : null);
				if (val != null && val.Size() >= 32768)
				{
					ClientStutterGuard.MarkFullSnapshotWindow();
					break;
				}
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "SetTarget")]
		[HarmonyPostfix]
		private static void SetTarget_Postfix(Character attacker)
		{
			if (Object.op_Implicit((Object)(object)attacker) && attacker.IsPlayer())
			{
				ClientStutterGuard.MarkCombatWindow();
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[HarmonyPostfix]
		private static void NewConnection_Postfix()
		{
			ClientStutterGuard.MarkTeleportWindow();
		}
	}
	[HarmonyPatch]
	public static class ZDOMan_Patch
	{
		private enum SyncPriority : byte
		{
			Default,
			Piece,
			Creature,
			Ship,
			Player
		}

		private static int _cursor;

		private static float _adaptiveBudget;

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

		public static void SafeSendZDOToPeers(ZDOMan man, float dt)
		{
			try
			{
				int count = man.m_peers.Count;
				if (count == 0)
				{
					_cursor = 0;
					man.m_nextSendPeer = -1;
					return;
				}
				if (_cursor >= count)
				{
					_cursor = 0;
				}
				float sendInterval = Helper.GetSendInterval();
				float num = 1f / sendInterval;
				_adaptiveBudget += (float)count * dt * num;
				int num2 = Mathf.FloorToInt(_adaptiveBudget);
				_adaptiveBudget -= num2;
				if (num2 > count)
				{
					num2 = count;
					_adaptiveBudget = 0f;
				}
				int value = VBNetTweaks.c_MaxPeersPerFrame.Value;
				if (value > 0 && num2 > value)
				{
					num2 = value;
				}
				if (num2 <= 0)
				{
					man.m_nextSendPeer = -1;
					return;
				}
				int queueLimit = Helper.GetQueueLimit();
				int num3 = 0;
				for (int i = 0; i < num2; i++)
				{
					int index = (_cursor + i) % count;
					ZDOPeer val = man.m_peers[index];
					if (val != null && val.m_peer != null && val.m_peer.m_socket != null && val.m_peer.m_socket.IsConnected() && val.m_peer.m_socket.GetSendQueueSize() <= queueLimit)
					{
						try
						{
							man.SendZDOs(val, false);
							num3++;
						}
						catch (Exception ex)
						{
							Helper.LogDebug($"SendZDOs failed for peer {val.m_peer.m_uid}: {ex.Message}");
						}
					}
				}
				_cursor = (_cursor + num2) % count;
				man.m_nextSendPeer = -1;
				NetStats.PeersServiced += num3;
			}
			catch (Exception ex2)
			{
				Helper.LogDebug("Error in SafeSendZDOToPeers: " + ex2.Message);
			}
		}

		private static int CustomCompare(ZDO x, ZDO y)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Invalid comparison between Unknown and I4
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Invalid comparison between Unknown and I4
			//IL_009d: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected I4, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Expected I4, but got Unknown
			if (x == null || y == null)
			{
				return 0;
			}
			bool flag = (int)x.Type == 1 && x.HasOwner() && x.GetOwner() != ZDOMan.s_compareReceiver;
			bool flag2 = (int)y.Type == 1 && y.HasOwner() && y.GetOwner() != ZDOMan.s_compareReceiver;
			if (flag && flag2)
			{
				return CompareFloats(x.m_tempSortValue, y.m_tempSortValue);
			}
			if (flag != flag2)
			{
				return (!flag) ? 1 : (-1);
			}
			if (x.Type != y.Type)
			{
				return ((int)y.Type).CompareTo((int)x.Type);
			}
			SyncPriority priority = GetPriority(x);
			SyncPriority priority2 = GetPriority(y);
			if (priority != priority2)
			{
				byte b = (byte)priority2;
				return b.CompareTo((byte)priority);
			}
			return CompareFloats(x.m_tempSortValue, y.m_tempSortValue);
		}

		private static int CompareFloats(float a, float b)
		{
			if (a < b)
			{
				return -1;
			}
			if (a > b)
			{
				return 1;
			}
			return 0;
		}

		private static SyncPriority GetPriority(ZDO zdo)
		{
			int prefab = zdo.GetPrefab();
			if (_priorityCache.TryGetValue(prefab, out var value))
			{
				return value;
			}
			SyncPriority syncPriority = SyncPriority.Default;
			if (Object.op_Implicit((Object)(object)ZNetScene.instance))
			{
				GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
				if (Object.op_Implicit((Object)(object)prefab2))
				{
					if (Object.op_Implicit((Object)(object)prefab2.GetComponent<Player>()))
					{
						syncPriority = SyncPriority.Player;
					}
					else if (Object.op_Implicit((Object)(object)prefab2.GetComponent<Ship>()))
					{
						syncPriority = SyncPriority.Ship;
					}
					else if (Object.op_Implicit((Object)(object)prefab2.GetComponent<Character>()))
					{
						syncPriority = SyncPriority.Creature;
					}
					else if (Object.op_Implicit((Object)(object)prefab2.GetComponent<Piece>()))
					{
						syncPriority = SyncPriority.Piece;
					}
				}
				_priorityCache[prefab] = syncPriority;
			}
			return syncPriority;
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOToPeers2")]
		[HarmonyPrefix]
		private static bool ZDOMan_SendZDOToPeers2_Patch(ZDOMan __instance, float dt)
		{
			if (!VBNetTweaks.c_ModuleZDOOptimization.Value)
			{
				return true;
			}
			SafeSendZDOToPeers(__instance, dt);
			return false;
		}

		[HarmonyPatch(typeof(ZDOMan), "SendZDOs")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SendZDOs_QueueLimitFix(IEnumerable<CodeInstruction> instructions)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			MethodInfo methodInfo = AccessTools.Method(typeof(Helper), "GetQueueLimit", (Type[])null, (Type[])null);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldc_I4 && list[i].operand is int num2 && num2 == 10240)
				{
					CodeInstruction val = new CodeInstruction(OpCodes.Call, (object)methodInfo);
					val.labels.AddRange(list[i].labels);
					val.blocks.AddRange(list[i].blocks);
					list[i] = val;
					num++;
				}
			}
			if (num < 2)
			{
				Helper.LogDebug($"ZDOQueueLimit patch WARNING: only {num}/2 replacements found!");
			}
			else
			{
				Helper.LogDebug($"ZDOQueueLimit patch OK: {num} replacements, limit={VBNetTweaks.c_ZDOQueueLimit.Value}");
			}
			return list;
		}

		[HarmonyPatch(typeof(ZDOMan), "ServerSendCompare")]
		[HarmonyPrefix]
		private static bool CustomServerSendCompare(ZDO x, ZDO y, ref int __result)
		{
			if (!VBNetTweaks.c_ModuleZDOOptimization.Value)
			{
				return true;
			}
			if (!ZNet.instance.IsServer())
			{
				return true;
			}
			__result = CustomCompare(x, y);
			return false;
		}

		[HarmonyPatch(typeof(ZDOMan), "ShutDown")]
		[HarmonyPostfix]
		private static void ClearCaches()
		{
			_priorityCache.Clear();
			_cursor = 0;
			_adaptiveBudget = 0f;
		}
	}
	[HarmonyPatch]
	public class ZNet_Patch
	{
		[HarmonyPatch(typeof(ZNet), "Start")]
		[HarmonyPostfix]
		private static void ZNet_Start_Patch()
		{
			if (Helper.IsDedicated())
			{
				Application.targetFrameRate = VBNetTweaks.c_TargetFPS.Value;
			}
		}

		[HarmonyPatch(typeof(ZNet), "SendPeriodicData")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> SendPeriodicData_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			bool flag = false;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Ldc_R4 && list[i].operand is float num && Math.Abs(num - 2f) < 0.001f)
				{
					list[i].opcode = OpCodes.Call;
					list[i].operand = AccessTools.Method(typeof(Helper), "GetMapPositionSendInterval", (Type[])null, (Type[])null);
					flag = true;
				}
			}
			if (!flag)
			{
				Helper.LogDebug("[MapPositionSync] SendPeriodicData constant 2f not found!");
			}
			return list;
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[HarmonyPostfix]
		private static void OnNewConnection_Postfix(ZNet __instance, ZNetPeer peer)
		{
			if (VBNetTweaks.c_ModEnabled.Value && !__instance.IsServer())
			{
				List<ZPackage> packages = new List<ZPackage>();
				Helper._buffers[peer.m_rpc] = packages;
				peer.m_rpc.Register<ZPackage>("ZDOData", (Action<ZRpc, ZPackage>)delegate(ZRpc _, ZPackage pkg)
				{
					packages.Add(pkg);
				});
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		[HarmonyPostfix]
		private static void Shutdown_Postfix()
		{
			Helper._buffers.Clear();
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect", new Type[] { typeof(ZNetPeer) })]
		[HarmonyPostfix]
		private static void Disconnect_Postfix(ZNetPeer peer)
		{
			Helper._buffers.Remove(peer.m_rpc);
		}

		[HarmonyPatch(typeof(ZNet), "OnDestroy")]
		[HarmonyPostfix]
		private static void ClearTracks()
		{
			MiniMap_Patch._playerTracks.Clear();
		}
	}
	[HarmonyPatch]
	public static class ZSteamSocket_Patch
	{
		[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
		[HarmonyPostfix]
		private static void ApplySteamSettings()
		{
			if (VBNetTweaks.c_ModuleSteamOptimizations.Value)
			{
				SetConfigFloat((ESteamNetworkingConfigValue)25, Helper.GetTimeoutConnected());
				SetConfigFloat((ESteamNetworkingConfigValue)24, 30000f);
				SetConfigInt((ESteamNetworkingConfigValue)11, Helper.GetSteamSendRateMaxKB());
				SetConfigInt((ESteamNetworkingConfigValue)9, Helper.GetSteamSendBufferSizeKB());
			}
		}

		[HarmonyPatch(typeof(ZSteamSocket), "Send")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Send_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSendQueuedPackages(instructions);
		}

		[HarmonyPatch(typeof(ZSteamSocket), "Flush")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Flush_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSendQueuedPackages(instructions);
		}

		[HarmonyPatch(typeof(ZSteamSocket), "Update")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Update_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return ReplaceSendQueuedPackages(instructions);
		}

		private static IEnumerable<CodeInstruction> ReplaceSendQueuedPackages(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ZSteamSocket_Patch), (Helper.IsDedicated() || Helper.IsServer()) ? "Replacement_Server" : "Replacement_Client", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ZSteamSocket), "SendQueuedPackages", (Type[])null, (Type[])null);
			CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null);
			val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
			{
				new CodeMatch((OpCode?)OpCodes.Call, (object)methodInfo2, (string)null)
			});
			if (!val.IsValid)
			{
				val.Start();
				val.MatchForward(false, (CodeMatch[])(object)new CodeMatch[1]
				{
					new CodeMatch((OpCode?)OpCodes.Callvirt, (object)methodInfo2, (string)null)
				});
			}
			if (!val.IsValid)
			{
				Debug.LogError((object)"NetworkSpeedup: Failed to find ZSteamSocket.SendQueuedPackages call");
				return val.InstructionEnumeration();
			}
			val.SetInstruction(new CodeInstruction(OpCodes.Call, (object)methodInfo));
			return val.InstructionEnumeration();
		}

		private unsafe static void Replacement_Client(ZSteamSocket socket)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Invalid comparison between Unknown and I4
			if (!socket.IsConnected() || socket.m_con == HSteamNetConnection.Invalid)
			{
				return;
			}
			long num = default(long);
			while (socket.m_sendQueue.Count > 0)
			{
				byte[] array = socket.m_sendQueue.Peek();
				if (array == null || array.Length == 0)
				{
					socket.m_sendQueue.Dequeue();
					continue;
				}
				EResult val;
				fixed (byte* ptr = array)
				{
					val = SteamNetworkingSockets.SendMessageToConnection(socket.m_con, (IntPtr)ptr, (uint)array.Length, 8, ref num);
				}
				if ((int)val != 1)
				{
					NetStats.SteamSendFail++;
					ZLog.Log((object)("Failed to send data " + ((object)(*(EResult*)(&val))/*cast due to .constrained prefix*/).ToString()));
					break;
				}
				socket.m_totalSent += array.Length;
				socket.m_sendQueue.Dequeue();
			}
		}

		private unsafe static void Replacement_Server(ZSteamSocket socket)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Invalid comparison between Unknown and I4
			if (!socket.IsConnected())
			{
				return;
			}
			long num = default(long);
			while (socket.m_sendQueue.Count > 0)
			{
				byte[] array = socket.m_sendQueue.Peek();
				if (array == null || array.Length == 0)
				{
					socket.m_sendQueue.Dequeue();
					continue;
				}
				EResult val;
				fixed (byte* ptr = array)
				{
					val = SteamGameServerNetworkingSockets.SendMessageToConnection(socket.m_con, (IntPtr)ptr, (uint)array.Length, 8, ref num);
				}
				if ((int)val != 1)
				{
					NetStats.SteamSendFail++;
					ZLog.Log((object)("Failed to send data " + ((object)(*(EResult*)(&val))/*cast due to .constrained prefix*/).ToString()));
					break;
				}
				socket.m_totalSent += array.Length;
				socket.m_sendQueue.Dequeue();
			}
		}

		private static void SetConfigFloat(ESteamNetworkingConfigValue config, float value)
		{
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected I4, but got Unknown
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected I4, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected I4, but got Unknown
			try
			{
				IntPtr intPtr = Marshal.AllocHGlobal(4);
				try
				{
					Marshal.StructureToPtr(value, intPtr, fDeleteOld: false);
					if ((Helper.IsDedicated() || Helper.IsServer()) ? SteamGameServerNetworkingUtils.SetConfigValue(config, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)3, intPtr) : SteamNetworkingUtils.SetConfigValue(config, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)3, intPtr))
					{
						Helper.LogDebug($"Steam cfg {(int)config} = {value:F2} — OK");
					}
					else
					{
						Helper.LogDebug($"Steam cfg {(int)config} = {value:F2} — ОТКЛОНЕНО (нативный SDK не знает этот параметр)");
					}
				}
				finally
				{
					Marshal.FreeHGlobal(intPtr);
				}
			}
			catch (Exception ex)
			{
				Helper.LogDebug($"Steam cfg {(int)config} — исключение: {ex.Message}");
			}
		}

		private static void SetConfigInt(ESteamNetworkingConfigValue config, int value)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected I4, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected I4, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected I4, but got Unknown
			try
			{
				IntPtr intPtr = Marshal.AllocHGlobal(4);
				try
				{
					Marshal.WriteInt32(intPtr, value);
					if ((Helper.IsDedicated() || Helper.IsServer()) ? SteamGameServerNetworkingUtils.SetConfigValue(config, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, intPtr) : SteamNetworkingUtils.SetConfigValue(config, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, intPtr))
					{
						Helper.LogDebug($"Steam cfg {(int)config} = {value} — OK");
					}
					else
					{
						Helper.LogDebug($"Steam cfg {(int)config} = {value} — ОТКЛОНЕНО (нативный SDK не знает этот параметр)");
					}
				}
				finally
				{
					Marshal.FreeHGlobal(intPtr);
				}
			}
			catch (Exception ex)
			{
				Helper.LogDebug($"Steam cfg {(int)config} — исключение: {ex.Message}");
			}
		}
	}
	[HarmonyPatch]
	public static class ZSyncTransform_Patch
	{
		[HarmonyPatch(typeof(ZSyncTransform), "SyncPosition")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SyncPosition_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Expected O, but got Unknown
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			MethodInfo methodInfo = AccessTools.Method(typeof(SyncTuning), "GetMaxExtrapolationSeconds", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SyncTuning), "GetSnapDistance", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(SyncTuning), "GetInterpFactor", (Type[])null, (Type[])null);
			float num4 = default(float);
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (val.opcode != OpCodes.Ldc_R4)
				{
					continue;
				}
				object operand = val.operand;
				int num5;
				if (operand is float)
				{
					num4 = (float)operand;
					num5 = 1;
				}
				else
				{
					num5 = 0;
				}
				if (num5 != 0)
				{
					if (Mathf.Approximately(num4, 2f))
					{
						list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
						num++;
					}
					else if (Mathf.Approximately(num4, 5f))
					{
						list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
						num2++;
					}
					else if (Mathf.Approximately(num4, 0.2f))
					{
						list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo3);
						num3++;
					}
				}
			}
			Helper.LogDebug($"[ZSync] SyncPosition transpiler: cap={num} snap={num2} lerp={num3}");
			return list;
		}

		[HarmonyPatch(typeof(ZSyncTransform), "ClientSync")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> ClientSync_Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = 0;
			MethodInfo methodInfo = AccessTools.Method(typeof(SyncTuning), "GetInterpFactor", (Type[])null, (Type[])null);
			for (int i = 0; i < list.Count; i++)
			{
				CodeInstruction val = list[i];
				if (val.opcode == OpCodes.Ldc_R4 && val.operand is float num2 && Mathf.Approximately(num2, 0.2f))
				{
					list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo);
					num++;
				}
			}
			Helper.LogDebug($"[ZSync] ClientSync transpiler: lerp={num}");
			return list;
		}
	}
}