Decompiled source of WarheimNetwork v2.2.1

WarheimNetwork.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("WarheimNetwork")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WarheimNetwork")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("2.2.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.2.1.0")]
namespace WarheimNetwork;

internal static class ControlPlaneGuard
{
	private static bool _installed;

	private static int _protectedAssemblies;

	private static int _protectedStateMachines;

	internal static void Install(Harmony harmony)
	{
		if (!_installed)
		{
			_installed = true;
			PatchZRpcTimeout(harmony);
			PatchEmbeddedServerSync(harmony);
			ApplyRuntimeSettings("installation");
			WarheimNetwork.Log.LogInfo((object)($"[Control] Protection installée : ServerSync={_protectedStateMachines} coroutine(s) dans " + $"{_protectedAssemblies} assembly(s), timeout={NetworkConfig.EffectiveControlPlaneTimeoutSeconds():F0}s."));
		}
	}

	internal static void ApplyRuntimeSettings(string reason)
	{
		try
		{
			Type type = AccessTools.TypeByName("Jotunn.Entities.CustomRPC, Jotunn");
			FieldInfo fieldInfo = ((type == null) ? null : AccessTools.Field(type, "Timeout"));
			if (fieldInfo == null)
			{
				WarheimNetwork.Log.LogWarning((object)"[Control] Jötunn CustomRPC.Timeout introuvable.");
				return;
			}
			float num = (NetworkConfig.IsModuleEnabled(NetworkConfig.ControlPlaneGuardEnabled) ? NetworkConfig.EffectiveControlPlaneTimeoutSeconds() : 30f);
			fieldInfo.SetValue(null, num);
			if (!(reason == "installation"))
			{
				ConfigEntry<bool> verboseLogging = NetworkConfig.VerboseLogging;
				if (verboseLogging == null || !verboseLogging.Value)
				{
					return;
				}
			}
			WarheimNetwork.Log.LogInfo((object)$"[Control] Jötunn timeout={num:F0}s ({reason}).");
		}
		catch (Exception ex)
		{
			WarheimNetwork.Log.LogWarning((object)("[Control] Application du timeout Jötunn impossible : " + ex.GetType().Name + ": " + ex.Message));
		}
	}

	private static void PatchZRpcTimeout(Harmony harmony)
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(typeof(ZRpc), "SetLongTimeout", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(ControlPlaneGuard), "ZRpcTimeoutTranspiler", (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo2 == null)
		{
			WarheimNetwork.Log.LogError((object)"[Control] Patch ZRpc.SetLongTimeout impossible : méthode introuvable.");
			return;
		}
		try
		{
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null);
			WarheimNetwork.Log.LogInfo((object)"[Control] Timeout ZRpc dynamique installé avec validation stricte.");
		}
		catch (Exception ex)
		{
			WarheimNetwork.Log.LogError((object)("[Control] Patch ZRpc.SetLongTimeout refusé : " + ex.GetType().Name + ": " + ex.Message));
		}
	}

	private static void PatchEmbeddedServerSync(Harmony harmony)
	{
		//IL_024c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.Method(typeof(ControlPlaneGuard), "ConfigSyncTimeoutTranspiler", (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			WarheimNetwork.Log.LogError((object)"[Control] Transpiler ServerSync introuvable.");
			return;
		}
		HashSet<Assembly> hashSet = new HashSet<Assembly>();
		foreach (PluginInfo value in Chainloader.PluginInfos.Values)
		{
			if ((Object)(object)((value != null) ? value.Instance : null) == (Object)null)
			{
				continue;
			}
			BepInPlugin metadata = value.Metadata;
			if (((metadata != null) ? metadata.GUID : null) == "dzk.warheimnetwork")
			{
				continue;
			}
			BepInPlugin metadata2 = value.Metadata;
			if (((metadata2 != null) ? metadata2.GUID : null) == "com.jotunn.jotunn")
			{
				continue;
			}
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			if (assembly == null || !hashSet.Add(assembly))
			{
				continue;
			}
			List<Type> types = GetLoadableTypes(assembly).ToList();
			List<Type> list = FindServerSyncStateMachines(types);
			if (list.Count == 0 && value.Metadata.GUID == "Azumatt.AzuAntiCheat")
			{
				list = FindAzuWaitForQueueStateMachines(types);
			}
			int num = 0;
			foreach (Type item in list)
			{
				MethodInfo methodInfo2 = AccessTools.Method(item, "MoveNext", (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					continue;
				}
				if (!HasConfigSyncTimeoutPattern(methodInfo2, out var reason))
				{
					if (value.Metadata.GUID == "Azumatt.AzuAntiCheat")
					{
						WarheimNetwork.Log.LogInfo((object)("[Control] Candidat AzuAntiCheat ignoré sans patch : " + item.FullName + " : " + reason));
						continue;
					}
					ConfigEntry<bool> verboseLogging = NetworkConfig.VerboseLogging;
					if (verboseLogging != null && verboseLogging.Value)
					{
						WarheimNetwork.Log.LogWarning((object)("[Control] Candidat ServerSync ignoré pour " + value.Metadata.Name + "/" + item.FullName + " : " + reason));
					}
					continue;
				}
				try
				{
					harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null);
					num++;
					_protectedStateMachines++;
				}
				catch (Exception ex)
				{
					WarheimNetwork.Log.LogWarning((object)("[Control] Protection ServerSync refusée pour " + value.Metadata.Name + "/" + item.FullName + " : " + ex.GetType().Name + ": " + ex.Message));
				}
			}
			if (num > 0)
			{
				_protectedAssemblies++;
				ConfigEntry<bool> verboseLogging2 = NetworkConfig.VerboseLogging;
				if (verboseLogging2 != null && verboseLogging2.Value)
				{
					WarheimNetwork.Log.LogInfo((object)$"[Control] {value.Metadata.Name} [{value.Metadata.GUID}] : {num} timeout(s) protégé(s).");
				}
			}
		}
	}

	private static bool HasConfigSyncTimeoutPattern(MethodBase method, out string reason)
	{
		try
		{
			List<CodeInstruction> currentInstructions = PatchProcessor.GetCurrentInstructions(method, int.MaxValue, (ILGenerator)null);
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Time), "time");
			if (methodInfo == null)
			{
				reason = "getter Time.time introuvable";
				return false;
			}
			int num = CountConfigSyncTimeoutPatterns(currentInstructions, methodInfo);
			if (num != 1)
			{
				reason = $"calcul Time.time+30 attendu une fois, trouvé {num} fois";
				return false;
			}
			reason = string.Empty;
			return true;
		}
		catch (Exception ex)
		{
			reason = "lecture IL impossible : " + ex.GetType().Name + ": " + ex.Message;
			return false;
		}
	}

	private static List<Type> FindServerSyncStateMachines(IEnumerable<Type> types)
	{
		List<Type> list = new List<Type>();
		foreach (Type item in types.Where((Type type) => type != null && type.IsClass && (type.Name == "ConfigSync" || type.Name == "ServerSync")))
		{
			foreach (Type item2 in GetNestedTypesRecursive(item))
			{
				if (item2.Name.IndexOf("waitForQueue", StringComparison.OrdinalIgnoreCase) >= 0 && AccessTools.Method(item2, "MoveNext", (Type[])null, (Type[])null) != null)
				{
					list.Add(item2);
				}
			}
		}
		return list.Distinct().ToList();
	}

	private static IEnumerable<Type> GetNestedTypesRecursive(Type root)
	{
		Stack<Type> pending;
		try
		{
			pending = new Stack<Type>(root.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic));
		}
		catch
		{
			yield break;
		}
		while (pending.Count > 0)
		{
			Type current = pending.Pop();
			yield return current;
			Type[] nested;
			try
			{
				nested = current.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
			}
			catch
			{
				continue;
			}
			Type[] array = nested;
			foreach (Type type in array)
			{
				pending.Push(type);
			}
		}
	}

	private static List<Type> FindAzuWaitForQueueStateMachines(IEnumerable<Type> types)
	{
		try
		{
			return (from type in (from method in types.SelectMany((Type type) => type.GetNestedTypes(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).SelectMany((Type type) => type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
					where method.Name.IndexOf("waitForQueue", StringComparison.OrdinalIgnoreCase) >= 0
					select method).SelectMany((MethodInfo method) => (method.DeclaringType == null) ? ((IEnumerable<Type>)Array.Empty<Type>()) : ((IEnumerable<Type>)method.DeclaringType.GetNestedTypes(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)))
				where AccessTools.Method(type, "MoveNext", (Type[])null, (Type[])null) != null
				select type).Distinct().ToList();
		}
		catch
		{
			return new List<Type>();
		}
	}

	private static IEnumerable<Type> GetLoadableTypes(Assembly assembly)
	{
		try
		{
			return assembly.GetTypes();
		}
		catch (ReflectionTypeLoadException ex)
		{
			return ex.Types.Where((Type type) => type != null);
		}
		catch
		{
			return Array.Empty<Type>();
		}
	}

	private static IEnumerable<CodeInstruction> ZRpcTimeoutTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		List<int> list2 = FindFloatConstants(list, 30f);
		List<int> list3 = FindFloatConstants(list, 90f);
		if (list2.Count != 1 || list3.Count != 1)
		{
			throw new InvalidOperationException(original?.DeclaringType?.FullName + "." + original?.Name + ": constantes 30/90 attendues 1/1, " + $"trouvées {list2.Count}/{list3.Count}");
		}
		MethodInfo methodInfo = AccessTools.Method(typeof(NetworkConfig), "EffectiveRpcShortTimeoutSeconds", (Type[])null, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(NetworkConfig), "EffectiveRpcLongTimeoutSeconds", (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo2 == null)
		{
			throw new MissingMethodException("Getters de timeout ZRpc introuvables");
		}
		ReplaceWithCall(list[list2[0]], methodInfo);
		ReplaceWithCall(list[list3[0]], methodInfo2);
		return list;
	}

	private static IEnumerable<CodeInstruction> ConfigSyncTimeoutTranspiler(IEnumerable<CodeInstruction> instructions, MethodBase original)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(Time), "time");
		MethodInfo methodInfo2 = AccessTools.Method(typeof(NetworkConfig), "EffectiveControlPlaneTimeoutSeconds", (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo2 == null)
		{
			throw new MissingMethodException("Getters de temps ou de timeout introuvables");
		}
		List<int> list2 = new List<int>();
		for (int i = 0; i + 2 < list.Count; i++)
		{
			if (CodeInstructionExtensions.Calls(list[i], methodInfo) && IsFloatConstant(list[i + 1], 30f) && !(list[i + 2].opcode != OpCodes.Add))
			{
				list2.Add(i + 1);
			}
		}
		if (list2.Count != 1)
		{
			WarheimNetwork.Log.LogWarning((object)("[Control] Transpiler ServerSync annulé pour " + original?.DeclaringType?.FullName + "." + original?.Name + " : " + $"calcul Time.time+30 attendu une fois, trouvé {list2.Count} fois."));
			return list;
		}
		ReplaceWithCall(list[list2[0]], methodInfo2);
		RewriteStandardDisconnectMessage(list, methodInfo2, original);
		return list;
	}

	private static int CountConfigSyncTimeoutPatterns(List<CodeInstruction> instructions, MethodInfo getTime)
	{
		int num = 0;
		for (int i = 0; i + 2 < instructions.Count; i++)
		{
			if (CodeInstructionExtensions.Calls(instructions[i], getTime) && IsFloatConstant(instructions[i + 1], 30f) && instructions[i + 2].opcode == OpCodes.Add)
			{
				num++;
			}
		}
		return num;
	}

	private static void RewriteStandardDisconnectMessage(List<CodeInstruction> instructions, MethodInfo timeoutGetter, MethodBase original)
	{
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0241: Expected O, but got Unknown
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		//IL_025f: Expected O, but got Unknown
		List<int> list = new List<int>();
		for (int i = 0; i < instructions.Count; i++)
		{
			if (instructions[i].opcode == OpCodes.Ldstr && object.Equals(instructions[i].operand, "Disconnecting {0} after 30 seconds config sending timeout"))
			{
				list.Add(i);
			}
		}
		if (list.Count == 0)
		{
			return;
		}
		if (list.Count != 1)
		{
			WarheimNetwork.Log.LogWarning((object)("[Control] Message ServerSync inchangé pour " + original?.DeclaringType?.FullName + " : " + $"1 occurrence attendue, {list.Count} trouvées."));
			return;
		}
		int num = -1;
		for (int j = list[0] + 1; j < Math.Min(instructions.Count, list[0] + 24); j++)
		{
			if (!(instructions[j].opcode != OpCodes.Call) && instructions[j].operand is MethodInfo methodInfo && !(methodInfo.DeclaringType != typeof(string)) && !(methodInfo.Name != "Format") && methodInfo.GetParameters().Length == 2)
			{
				num = j;
				break;
			}
		}
		MethodInfo methodInfo2 = AccessTools.Method(typeof(string), "Format", new Type[3]
		{
			typeof(string),
			typeof(object),
			typeof(object)
		}, (Type[])null);
		if (num < 0 || methodInfo2 == null)
		{
			WarheimNetwork.Log.LogWarning((object)("[Control] Message ServerSync inchangé pour " + original?.DeclaringType?.FullName + " : string.Format introuvable."));
			return;
		}
		instructions[list[0]].operand = "Disconnecting {0} after {1:F0} seconds config sending timeout";
		instructions.Insert(num, new CodeInstruction(OpCodes.Call, (object)timeoutGetter));
		instructions.Insert(num + 1, new CodeInstruction(OpCodes.Box, (object)typeof(float)));
		instructions[num + 2].operand = methodInfo2;
	}

	private static List<int> FindFloatConstants(List<CodeInstruction> instructions, float value)
	{
		List<int> list = new List<int>();
		for (int i = 0; i < instructions.Count; i++)
		{
			if (IsFloatConstant(instructions[i], value))
			{
				list.Add(i);
			}
		}
		return list;
	}

	private static bool IsFloatConstant(CodeInstruction instruction, float value)
	{
		return instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num && Math.Abs(num - value) < 0.001f;
	}

	private static void ReplaceWithCall(CodeInstruction instruction, MethodInfo getter)
	{
		instruction.opcode = OpCodes.Call;
		instruction.operand = getter;
	}
}
[HarmonyPatch(typeof(ZoneSystem), "RPC_GlobalKeys")]
internal static class MapSyncGuard
{
	[HarmonyPrefix]
	private static void Prefix(ref MapMode __state)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		__state = (MapMode)(((Object)(object)Minimap.instance != (Object)null) ? ((int)Minimap.instance.m_mode) : 0);
	}

	[HarmonyPostfix]
	private static void Postfix(MapMode __state)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0003: Invalid comparison between Unknown and I4
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Invalid comparison between Unknown and I4
		if ((int)__state == 2 && !((Object)(object)Minimap.instance == (Object)null) && !MapIsDisabled() && (int)Minimap.instance.m_mode != 2)
		{
			Minimap.instance.SetMapMode((MapMode)2);
			NetworkDiagnostics.RecordMapModeRestore();
			WarheimNetwork.Log.LogInfo((object)"[MapSync] Grande carte restaurée après synchronisation des global keys.");
		}
	}

	private static bool MapIsDisabled()
	{
		if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)26))
		{
			return true;
		}
		Player localPlayer = Player.m_localPlayer;
		return (Object)(object)localPlayer != (Object)null && PlayerPrefs.GetFloat("mapenabled_" + localPlayer.GetPlayerName(), 1f) == 0f;
	}
}
internal static class NetworkConfig
{
	internal const int VanillaZdoQueueLimit = 10240;

	internal const float VanillaPositionSmooth = 0.2f;

	internal const float VanillaRotationSmooth = 0.5f;

	internal const float VanillaMicroThreshold = 0.001f;

	internal const float VanillaClientDistanceThreshold = 0.01f;

	internal static ConfigEntry<bool> MasterEnabled;

	internal static ConfigEntry<bool> SteamTransportEnabled;

	internal static ConfigEntry<bool> TransportCompressionEnabled;

	internal static ConfigEntry<bool> ZdoSchedulerEnabled;

	internal static ConfigEntry<bool> ZdoQueueLimitEnabled;

	internal static ConfigEntry<bool> AdaptiveBackpressureEnabled;

	internal static ConfigEntry<bool> ControlPlaneGuardEnabled;

	internal static ConfigEntry<bool> TransformSyncEnabled;

	internal static ConfigEntry<bool> LifecycleGuardEnabled;

	internal static ConfigEntry<bool> ShipSyncEnabled;

	internal static ConfigEntry<bool> SaveGuardEnabled;

	internal static ConfigEntry<int> SteamSendRateMaxKb;

	internal static ConfigEntry<int> SteamSendRateMinKb;

	internal static ConfigEntry<int> SteamSendBufferKb;

	internal static ConfigEntry<int> SteamReceiveBufferKb;

	internal static ConfigEntry<int> SteamReceiveMaxMessageKb;

	internal static ConfigEntry<int> CompressionThresholdBytes;

	internal static ConfigEntry<int> CompressionMinimumSavingsPercent;

	internal static ConfigEntry<int> CompressionMaxPackageMb;

	internal static ConfigEntry<float> ZdoSendInterval;

	internal static ConfigEntry<int> ZdoPeersPerUpdate;

	internal static ConfigEntry<int> ZdoQueueLimit;

	internal static ConfigEntry<int> ZdoMinimumPackageBytes;

	internal static ConfigEntry<int> ZdoMaximumPackageBytes;

	internal static ConfigEntry<int> ZdoQueueSoftLimit;

	internal static ConfigEntry<int> ZdoQueueHardLimit;

	internal static ConfigEntry<int> ZdoQueueEmergencyLimit;

	internal static ConfigEntry<float> ZdoMinimumPeerInterval;

	internal static ConfigEntry<float> ZdoMaximumPeerInterval;

	internal static ConfigEntry<float> ZdoMaximumSchedulerWorkMs;

	internal static ConfigEntry<float> ZdoMaximumStarvationSeconds;

	internal static ConfigEntry<float> ZdoCatchUpSeconds;

	internal static ConfigEntry<int> ZdoFlushThresholdPercent;

	internal static ConfigEntry<float> ControlPlaneTimeoutSeconds;

	internal static ConfigEntry<float> PeerDiagnosticsIntervalSeconds;

	internal static ConfigEntry<float> PositionSmooth;

	internal static ConfigEntry<float> RotationSmooth;

	internal static ConfigEntry<float> MicroMovementThreshold;

	internal static ConfigEntry<float> ClientDistanceThreshold;

	internal static ConfigEntry<bool> VerboseLogging;

	internal static event Action SteamSettingsChanged;

	internal static event Action CompressionSettingsChanged;

	internal static event Action ControlSettingsChanged;

	internal static void Bind(ConfigFile config)
	{
		MasterEnabled = ConfigFileExtensions.BindConfig<bool>(config, "00 - Général", "Enabled", true, "Active WarheimNetwork. Les patchs restent installés mais retombent sur le comportement vanilla.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		SteamTransportEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "SteamTransport", true, "Augmente les débits et buffers de SteamNetworkingSockets.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		TransportCompressionEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "TransportCompression", true, "Compresse les paquets Steam suffisamment gros après négociation avec chaque peer.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		ZdoSchedulerEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "ZdoScheduler", true, "Distribue équitablement les envois ZDO avec rattrapage des nouveaux peers.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		ZdoQueueLimitEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "ZdoQueueLimit", true, "Applique un budget ZDO dynamique propre à chaque peer.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		AdaptiveBackpressureEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "AdaptiveCongestion", true, "Adapte chaque peer à partir de sa file, de son débit et de l'inflation de son RTT.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		ControlPlaneGuardEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "ControlPlaneGuard", true, "Réserve de la place aux synchronisations de configuration et prolonge leurs timeouts.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		TransformSyncEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "TransformSync", true, "Applique les réglages de lissage réseau validés pour le PvP.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		LifecycleGuardEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "LifecycleGuard", true, "Récupère les références ZNetView détruites qui feraient boucler ZNetScene.RemoveObjects.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		ShipSyncEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "ShipSync", true, "Accélère la synchronisation du gouvernail et lisse les navires et joueurs attachés.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		SaveGuardEnabled = ConfigFileExtensions.BindConfig<bool>(config, "01 - Modules", "SaveGuard", true, "Force les sauvegardes dédiées synchrones, suspend le réseau et sérialise les ZDO sans listes temporaires lorsque le contrat vanilla est compatible.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		AcceptableValueBase val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(256, 32768);
		SteamSendRateMaxKb = ConfigFileExtensions.BindConfig<int>(config, "02 - Steam", "SendRateMaxKB", 16384, "Débit d'envoi maximal Steam en Ko/s.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(128, 32768);
		SteamSendRateMinKb = ConfigFileExtensions.BindConfig<int>(config, "02 - Steam", "SendRateMinKB", 256, "Débit d'envoi minimal demandé à Steam en Ko/s.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(512, 16384);
		SteamSendBufferKb = ConfigFileExtensions.BindConfig<int>(config, "02 - Steam", "SendBufferKB", 8192, "Taille du buffer d'envoi Steam en Ko.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(512, 16384);
		SteamReceiveBufferKb = ConfigFileExtensions.BindConfig<int>(config, "02 - Steam", "ReceiveBufferKB", 4096, "Taille du buffer de réception Steam en Ko.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1024, 32768);
		SteamReceiveMaxMessageKb = ConfigFileExtensions.BindConfig<int>(config, "02 - Steam", "ReceiveMaxMessageKB", 8192, "Taille maximale d'un message reçu par Steam en Ko.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(256, 65536);
		CompressionThresholdBytes = ConfigFileExtensions.BindConfig<int>(config, "03 - Compression", "ThresholdBytes", 1024, "Taille minimale d'un paquet avant tentative de compression.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50);
		CompressionMinimumSavingsPercent = ConfigFileExtensions.BindConfig<int>(config, "03 - Compression", "MinimumSavingsPercent", 10, "Gain minimal exigé pour envoyer un paquet compressé.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(4, 256);
		CompressionMaxPackageMb = ConfigFileExtensions.BindConfig<int>(config, "03 - Compression", "MaximumPackageMB", 64, "Taille maximale acceptée après décompression.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.1f);
		ZdoSendInterval = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "SendInterval", 0.02f, "Intervalle global entre cycles ZDO. Vanilla : 0.05 seconde.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200);
		ZdoPeersPerUpdate = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "PeersPerUpdate", 50, "Nombre maximal de peers examinés par cycle ZDO.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(10240, 262144);
		ZdoQueueLimit = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "InitialPackageBytes", 65536, "Budget ZDO initial par envoi et par peer.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(8192, 65536);
		ZdoMinimumPackageBytes = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "MinimumPackageBytes", 12288, "Budget ZDO minimal sous forte congestion.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(16384, 524288);
		ZdoMaximumPackageBytes = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "MaximumPackageBytes", 131072, "Budget ZDO maximal pendant un rattrapage sain.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(32768, 2097152);
		ZdoQueueSoftLimit = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "QueueSoftLimit", 131072, "File Steam à partir de laquelle la croissance ZDO ralentit.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(65536, 8388608);
		ZdoQueueHardLimit = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "QueueHardLimit", 524288, "File Steam au-dessus de laquelle aucun nouveau paquet ZDO n'est ajouté.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(262144, 33554432);
		ZdoQueueEmergencyLimit = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "QueueEmergencyLimit", 2097152, "File Steam signalant une congestion critique dans les diagnostics.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 0.1f);
		ZdoMinimumPeerInterval = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "MinimumPeerInterval", 0.015f, "Intervalle minimal d'un peer sain en rattrapage.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.03f, 1f);
		ZdoMaximumPeerInterval = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "MaximumPeerInterval", 0.15f, "Intervalle maximal d'un peer réellement congestionné.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 20f);
		ZdoMaximumSchedulerWorkMs = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "MaximumSchedulerWorkMs", 4f, "Temps CPU maximal consacré à un cycle ZDO.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f);
		ZdoMaximumStarvationSeconds = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "MaximumStarvationSeconds", 0.5f, "Délai maximal avant de prioriser un peer servi trop tard.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 120f);
		ZdoCatchUpSeconds = ConfigFileExtensions.BindConfig<float>(config, "04 - ZDO", "CatchUpSeconds", 30f, "Durée du profil de rattrapage après connexion.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 90);
		ZdoFlushThresholdPercent = ConfigFileExtensions.BindConfig<int>(config, "04 - ZDO", "FlushThresholdPercent", 40, "Seuil de file autorisant un envoi ZDO complet pendant le rattrapage.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 300f);
		ControlPlaneTimeoutSeconds = ConfigFileExtensions.BindConfig<float>(config, "05 - Contrôle", "TimeoutSeconds", 120f, "Timeout des RPC longs et des synchronisations ServerSync intégrées aux mods.", false, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f);
		PeerDiagnosticsIntervalSeconds = ConfigFileExtensions.BindConfig<float>(config, "05 - Contrôle", "PeerDiagnosticsInterval", 10f, "Intervalle des diagnostics détaillés par peer lorsque VerboseLogging est actif.", false, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f);
		PositionSmooth = ConfigFileExtensions.BindConfig<float>(config, "06 - Transform", "PositionSmooth", 0.22f, "Valeur de lissage de position distante. Vanilla : 0.20.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f);
		RotationSmooth = ConfigFileExtensions.BindConfig<float>(config, "06 - Transform", "RotationSmooth", 0.45f, "Valeur de lissage de rotation distante. Vanilla : 0.50.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.0001f, 0.05f);
		MicroMovementThreshold = ConfigFileExtensions.BindConfig<float>(config, "06 - Transform", "MicroMovementThreshold", 0.004f, "Seuil des micro-mouvements réseau. Vanilla : 0.001.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.0001f, 0.05f);
		ClientDistanceThreshold = ConfigFileExtensions.BindConfig<float>(config, "06 - Transform", "ClientDistanceThreshold", 0.005f, "Seuil de distance de synchronisation client. Vanilla : 0.01.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		VerboseLogging = ConfigFileExtensions.BindConfig<bool>(config, "07 - Diagnostic", "VerboseLogging", false, "Ajoute des informations de diagnostic agrégées.", false, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		WatchSteamEntry<bool>(MasterEnabled);
		WatchCompressionEntry<bool>(MasterEnabled);
		WatchControlEntry<bool>(MasterEnabled);
		WatchSteamEntry<bool>(SteamTransportEnabled);
		WatchSteamEntry<int>(SteamSendRateMaxKb);
		WatchSteamEntry<int>(SteamSendRateMinKb);
		WatchSteamEntry<int>(SteamSendBufferKb);
		WatchSteamEntry<int>(SteamReceiveBufferKb);
		WatchSteamEntry<int>(SteamReceiveMaxMessageKb);
		WatchCompressionEntry<bool>(TransportCompressionEnabled);
		WatchCompressionEntry<int>(CompressionThresholdBytes);
		WatchCompressionEntry<int>(CompressionMinimumSavingsPercent);
		WatchControlEntry<bool>(ControlPlaneGuardEnabled);
		WatchControlEntry<float>(ControlPlaneTimeoutSeconds);
	}

	private static void WatchSteamEntry<T>(ConfigEntry<T> entry)
	{
		entry.SettingChanged += delegate
		{
			NetworkConfig.SteamSettingsChanged?.Invoke();
		};
	}

	private static void WatchCompressionEntry<T>(ConfigEntry<T> entry)
	{
		entry.SettingChanged += delegate
		{
			NetworkConfig.CompressionSettingsChanged?.Invoke();
		};
	}

	private static void WatchControlEntry<T>(ConfigEntry<T> entry)
	{
		entry.SettingChanged += delegate
		{
			NetworkConfig.ControlSettingsChanged?.Invoke();
		};
	}

	internal static bool IsModuleEnabled(ConfigEntry<bool> module)
	{
		ConfigEntry<bool> masterEnabled = MasterEnabled;
		return masterEnabled != null && masterEnabled.Value && (module?.Value ?? false);
	}

	internal static int EffectiveSteamSendRateMaxBytes()
	{
		if (!IsModuleEnabled(SteamTransportEnabled))
		{
			return 153600;
		}
		return Math.Max(SteamSendRateMaxKb.Value, SteamSendRateMinKb.Value) * 1024;
	}

	internal static int EffectiveZdoQueueTarget()
	{
		if (!IsModuleEnabled(ZdoQueueLimitEnabled))
		{
			return 10240;
		}
		return PeerTrafficController.EffectiveQueueTarget(ZdoQueueLimit.Value);
	}

	internal static int EffectiveZdoQueueGate()
	{
		if (!IsModuleEnabled(ZdoQueueLimitEnabled))
		{
			return 10240;
		}
		return PeerTrafficController.EffectiveQueueGate(ZdoQueueSoftLimit.Value);
	}

	internal static float EffectiveControlPlaneTimeoutSeconds()
	{
		return IsModuleEnabled(ControlPlaneGuardEnabled) ? Math.Max(60f, ControlPlaneTimeoutSeconds.Value) : 30f;
	}

	internal static float EffectiveRpcShortTimeoutSeconds()
	{
		return IsModuleEnabled(ControlPlaneGuardEnabled) ? Math.Max(60f, ControlPlaneTimeoutSeconds.Value) : 30f;
	}

	internal static float EffectiveRpcLongTimeoutSeconds()
	{
		return IsModuleEnabled(ControlPlaneGuardEnabled) ? Math.Max(60f, ControlPlaneTimeoutSeconds.Value) : 90f;
	}

	internal static float EffectivePositionSmooth()
	{
		return IsModuleEnabled(TransformSyncEnabled) ? PositionSmooth.Value : 0.2f;
	}

	internal static float EffectiveRotationSmooth()
	{
		return IsModuleEnabled(TransformSyncEnabled) ? RotationSmooth.Value : 0.5f;
	}

	internal static float EffectiveMicroThreshold()
	{
		return IsModuleEnabled(TransformSyncEnabled) ? MicroMovementThreshold.Value : 0.001f;
	}

	internal static float EffectiveClientDistanceThreshold()
	{
		return IsModuleEnabled(TransformSyncEnabled) ? ClientDistanceThreshold.Value : 0.01f;
	}
}
internal static class NetworkDiagnostics
{
	private static long _zdoCycles;

	private static long _zdoPeersAttempted;

	private static long _zdoPeersSent;

	private static long _zdoFlushes;

	private static long _zdoForced;

	private static long _zdoWorkLimitedCycles;

	private static long _lifecycleRecoveries;

	private static long _lifecycleInstancesPurged;

	private static long _lifecycleTemporaryPurged;

	private static long _shipRudderSends;

	private static long _shipAttachSnaps;

	private static long _saveBarriers;

	private static long _forcedSynchronousSaves;

	private static long _lowAllocationSnapshots;

	private static long _avoidedInnerClones;

	private static long _fastSerializedZdos;

	private static long _serializedExtraValues;

	private static long _adminSaveRequests;

	private static long _scheduledSaveRequests;

	private static long _systemSaveRequests;

	private static long _mapModeRestores;

	internal static void RecordZdoCycle(int attempted, int sent, int flushed, int forced, bool workLimited)
	{
		_zdoCycles++;
		_zdoPeersAttempted += attempted;
		_zdoPeersSent += sent;
		_zdoFlushes += flushed;
		_zdoForced += forced;
		if (workLimited)
		{
			_zdoWorkLimitedCycles++;
		}
	}

	internal static void RecordLifecycleRecovery(int instancesPurged, int temporaryPurged)
	{
		_lifecycleRecoveries++;
		_lifecycleInstancesPurged += Math.Max(0, instancesPurged);
		_lifecycleTemporaryPurged += Math.Max(0, temporaryPurged);
	}

	internal static void RecordShipRudderSend()
	{
		_shipRudderSends++;
	}

	internal static void RecordShipAttachSnap()
	{
		_shipAttachSnaps++;
	}

	internal static void RecordSaveBarrier()
	{
		_saveBarriers++;
	}

	internal static void RecordForcedSynchronousSave()
	{
		_forcedSynchronousSaves++;
	}

	internal static void RecordLowAllocationSaveSnapshot(long avoidedInnerClones)
	{
		Interlocked.Increment(ref _lowAllocationSnapshots);
		Interlocked.Add(ref _avoidedInnerClones, Math.Max(0L, avoidedInnerClones));
	}

	internal static void RecordFastSerializedZdo(int extraValues)
	{
		Interlocked.Increment(ref _fastSerializedZdos);
		Interlocked.Add(ref _serializedExtraValues, Math.Max(0, extraValues));
	}

	internal static void RecordSaveRequest(string origin)
	{
		if (string.Equals(origin, "commande admin vanilla", StringComparison.Ordinal))
		{
			Interlocked.Increment(ref _adminSaveRequests);
		}
		else if (origin != null && origin.IndexOf("autosave", StringComparison.OrdinalIgnoreCase) >= 0)
		{
			Interlocked.Increment(ref _scheduledSaveRequests);
		}
		else
		{
			Interlocked.Increment(ref _systemSaveRequests);
		}
	}

	internal static void RecordMapModeRestore()
	{
		_mapModeRestores++;
	}

	internal static void ResetSession()
	{
		_zdoCycles = 0L;
		_zdoPeersAttempted = 0L;
		_zdoPeersSent = 0L;
		_zdoFlushes = 0L;
		_zdoForced = 0L;
		_zdoWorkLimitedCycles = 0L;
		_lifecycleRecoveries = 0L;
		_lifecycleInstancesPurged = 0L;
		_lifecycleTemporaryPurged = 0L;
		_shipRudderSends = 0L;
		_shipAttachSnaps = 0L;
		_saveBarriers = 0L;
		_forcedSynchronousSaves = 0L;
		_lowAllocationSnapshots = 0L;
		_avoidedInnerClones = 0L;
		_fastSerializedZdos = 0L;
		_serializedExtraValues = 0L;
		_adminSaveRequests = 0L;
		_scheduledSaveRequests = 0L;
		_systemSaveRequests = 0L;
		_mapModeRestores = 0L;
	}

	internal static void LogSessionSummary()
	{
		if (_zdoCycles > 0)
		{
			WarheimNetwork.Log.LogInfo((object)($"[Diagnostic] Session ZDO : cycles={_zdoCycles}, " + $"peers examinés={_zdoPeersAttempted}, envois={_zdoPeersSent}, flush={_zdoFlushes}, " + $"forcés={_zdoForced}, cycles limités CPU={_zdoWorkLimitedCycles}."));
		}
		if (_lifecycleRecoveries > 0)
		{
			WarheimNetwork.Log.LogInfo((object)($"[Diagnostic] Session Lifecycle : récupérations={_lifecycleRecoveries}, " + $"instances purgées={_lifecycleInstancesPurged}, temporaires purgées={_lifecycleTemporaryPurged}."));
		}
		if (_shipRudderSends > 0 || _shipAttachSnaps > 0)
		{
			WarheimNetwork.Log.LogInfo((object)($"[Diagnostic] Session ShipSync : envois gouvernail supplémentaires={_shipRudderSends}, " + $"recalages joueurs={_shipAttachSnaps}."));
		}
		if (_saveBarriers > 0 || _lowAllocationSnapshots > 0)
		{
			WarheimNetwork.Log.LogInfo((object)($"[Diagnostic] Session SaveGuard : barrières={_saveBarriers}, synchrones forcées={_forcedSynchronousSaves}, " + $"requêtes admin={_adminSaveRequests}, programmées={_scheduledSaveRequests}, système={_systemSaveRequests}, " + $"snapshots faibles allocations={_lowAllocationSnapshots}, " + $"ZDO sérialisés sans listes={_fastSerializedZdos}, valeurs extra={_serializedExtraValues}, " + $"entrées/clones évités={_avoidedInnerClones}."));
		}
		if (_mapModeRestores > 0)
		{
			WarheimNetwork.Log.LogInfo((object)$"[Diagnostic] Session MapSync : grandes cartes restaurées={_mapModeRestores}.");
		}
		TransportCompression.LogSessionSummary();
	}

	internal static void LogEffectiveSettings(string reason)
	{
		double num = (double)NetworkConfig.ZdoMaximumPackageBytes.Value * (double)NetworkConfig.ZdoPeersPerUpdate.Value / (double)Math.Max(NetworkConfig.ZdoSendInterval.Value, 0.01f) / 1048576.0;
		WarheimNetwork.Log.LogInfo((object)($"[Config] {reason} | master={NetworkConfig.MasterEnabled.Value}, " + $"steam={NetworkConfig.IsModuleEnabled(NetworkConfig.SteamTransportEnabled)}, " + $"compression={NetworkConfig.IsModuleEnabled(NetworkConfig.TransportCompressionEnabled)} " + $"(seuil={NetworkConfig.CompressionThresholdBytes.Value}o, gain={NetworkConfig.CompressionMinimumSavingsPercent.Value}%), " + $"zdo={NetworkConfig.IsModuleEnabled(NetworkConfig.ZdoSchedulerEnabled)} " + $"({NetworkConfig.ZdoSendInterval.Value:F3}s/{NetworkConfig.ZdoPeersPerUpdate.Value} peers), " + $"paquet={NetworkConfig.ZdoMinimumPackageBytes.Value}->{NetworkConfig.ZdoQueueLimit.Value}->{NetworkConfig.ZdoMaximumPackageBytes.Value}, " + $"queue={NetworkConfig.ZdoQueueSoftLimit.Value}->{NetworkConfig.ZdoQueueHardLimit.Value}->{NetworkConfig.ZdoQueueEmergencyLimit.Value}, " + $"adaptive={NetworkConfig.IsModuleEnabled(NetworkConfig.AdaptiveBackpressureEnabled)}, " + $"control={NetworkConfig.IsModuleEnabled(NetworkConfig.ControlPlaneGuardEnabled)} " + $"({NetworkConfig.EffectiveControlPlaneTimeoutSeconds():F0}s), " + $"transform={NetworkConfig.IsModuleEnabled(NetworkConfig.TransformSyncEnabled)}, " + $"lifecycle={NetworkConfig.IsModuleEnabled(NetworkConfig.LifecycleGuardEnabled)}, " + $"ship={NetworkConfig.IsModuleEnabled(NetworkConfig.ShipSyncEnabled)}, " + $"saveGuard={NetworkConfig.IsModuleEnabled(NetworkConfig.SaveGuardEnabled)}, " + $"plafond ZDO théorique={num:F1} Mio/s."));
	}

	internal static void AuditSavePatches(string reason)
	{
		AuditSaveMethod(typeof(ZNet), "Save", reason);
		AuditSaveMethod(typeof(ZNet), "SaveWorld", reason);
		AuditSaveMethod(typeof(ZNet), "RPC_Save", reason);
		AuditSaveMethod(typeof(ZDOMan), "PrepareSave", reason);
		AuditSaveMethod(typeof(ZDOMan), "SaveAsync", reason);
		AuditSaveMethod(typeof(ZDOExtraData), "PrepareSave", reason);
		AuditSaveMethod(typeof(ZDO), "Save", reason);
	}

	private static void AuditSaveMethod(Type type, string methodName, string reason)
	{
		MethodBase methodBase = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
		if (methodBase == null)
		{
			WarheimNetwork.Log.LogWarning((object)("[AuditSave] " + type.Name + "." + methodName + " introuvable."));
			return;
		}
		Patches patchInfo = Harmony.GetPatchInfo(methodBase);
		if (patchInfo == null)
		{
			WarheimNetwork.Log.LogInfo((object)("[AuditSave] " + reason + " : " + type.Name + "." + methodName + " est vanilla."));
			return;
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		AddOwners(hashSet, patchInfo.Prefixes);
		AddOwners(hashSet, patchInfo.Postfixes);
		AddOwners(hashSet, patchInfo.Transpilers);
		AddOwners(hashSet, patchInfo.Finalizers);
		bool flag = hashSet.Remove("dzk.warheimnetwork");
		string text = ((hashSet.Count == 0) ? "aucun" : string.Join(", ", hashSet.OrderBy((string owner) => owner)));
		WarheimNetwork.Log.LogInfo((object)$"[AuditSave] {reason} : {type.Name}.{methodName}, WarheimNetwork={flag}, autres={text}.");
	}

	internal static void AuditLifecyclePatches(string reason)
	{
		AuditMethod(typeof(ZNetScene), "RemoveObjects", reason, allowSelf: true);
		AuditMethod(typeof(ZNetScene), "CreateDestroyObjects", reason, allowSelf: false);
		AuditMethod(typeof(ZNetScene), "InLoadingScreen", reason, allowSelf: false);
	}

	private static void AuditMethod(Type type, string methodName, string reason, bool allowSelf)
	{
		MethodBase methodBase = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
		if (methodBase == null)
		{
			WarheimNetwork.Log.LogWarning((object)("[Audit] " + type.Name + "." + methodName + " introuvable pour l'audit."));
			return;
		}
		Patches patchInfo = Harmony.GetPatchInfo(methodBase);
		if (patchInfo == null)
		{
			WarheimNetwork.Log.LogInfo((object)("[Audit] " + reason + " : " + type.Name + "." + methodName + " est entièrement vanilla."));
			return;
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		AddOwners(hashSet, patchInfo.Prefixes);
		AddOwners(hashSet, patchInfo.Postfixes);
		AddOwners(hashSet, patchInfo.Transpilers);
		AddOwners(hashSet, patchInfo.Finalizers);
		bool flag = hashSet.Remove("dzk.warheimnetwork");
		if (flag && !allowSelf)
		{
			WarheimNetwork.Log.LogError((object)("[Audit] ERREUR CRITIQUE : WarheimNetwork apparaît sur " + type.Name + "." + methodName + "."));
		}
		else if (flag)
		{
			WarheimNetwork.Log.LogInfo((object)("[Audit] " + reason + " : " + type.Name + "." + methodName + " conserve son corps vanilla avec finalizer de récupération WarheimNetwork."));
		}
		if (hashSet.Count == 0)
		{
			if (!flag)
			{
				WarheimNetwork.Log.LogInfo((object)("[Audit] " + reason + " : " + type.Name + "." + methodName + " est entièrement vanilla."));
			}
			return;
		}
		string text = string.Join(", ", hashSet.OrderBy((string owner) => owner));
		WarheimNetwork.Log.LogWarning((object)("[Audit] " + reason + " : autres patchs détectés sur " + type.Name + "." + methodName + " : " + text));
	}

	private static void AddOwners(HashSet<string> owners, IEnumerable<Patch> patches)
	{
		foreach (Patch patch in patches)
		{
			if (!string.IsNullOrEmpty(patch.owner))
			{
				owners.Add(patch.owner);
			}
		}
	}
}
[HarmonyPatch]
internal static class NetworkDiagnosticsPatches
{
	[HarmonyPatch(typeof(Game), "Start")]
	[HarmonyPostfix]
	private static void GameStartPostfix()
	{
		NetworkDiagnostics.ResetSession();
		PeerTrafficController.Reset();
		TransportCompression.Reset();
		ShipSyncPatches.Reset();
		SavePressureGuard.Reset();
		NetworkDiagnostics.AuditLifecyclePatches("Game.Start");
		NetworkDiagnostics.AuditSavePatches("Game.Start");
		SavePressureGuard.FinalizeCompatibilityAudit();
	}

	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	[HarmonyPrefix]
	private static void ZNetShutdownPrefix()
	{
		NetworkDiagnostics.LogSessionSummary();
		ShipSyncPatches.Reset();
		SavePressureGuard.Reset();
	}
}
internal enum PeerCongestionState
{
	Open,
	Loading,
	Congested,
	Emergency
}
internal static class PeerTrafficController
{
	internal sealed class PeerState
	{
		internal ZNetPeer Peer;

		internal PeerCongestionState State;

		internal float CreatedAt;

		internal float LastSeen;

		internal float LastSend;

		internal float NextEligibleSend;

		internal float NextQualitySample;

		internal float NextDiagnostic;

		internal float LastQueueSampleTime;

		internal int LastQueueSample;

		internal int Queue;

		internal int PeakQueue;

		internal float QueueEma;

		internal float QueueSlopeEma;

		internal int Ping = -1;

		internal float BaselinePing = -1f;

		internal float PingEma = -1f;

		internal float JitterEma;

		internal float Quality = -1f;

		internal float OutBytesPerSecond;

		internal float InBytesPerSecond;

		internal float Pressure;

		internal int PackageBudget;

		internal float SendInterval;

		internal int QueueBeforeSend;

		internal long SendAttempts;

		internal long SuccessfulSends;

		internal long FlushSends;

		internal long ForcedSends;

		internal long SkippedForQueue;

		internal long SkippedForInterval;

		internal long BytesEnqueued;

		internal long EmergencySamples;
	}

	private const float QualitySampleInterval = 1f;

	private const float StalePeerSeconds = 10f;

	private static readonly Dictionary<ZNetPeer, PeerState> States = new Dictionary<ZNetPeer, PeerState>();

	[ThreadStatic]
	private static PeerState _activeSend;

	private static float _nextMaintenance;

	internal static int ActivePeerCount => States.Count;

	internal static bool TryPrepareSend(ZNetPeer peer, out PeerState state, out int queueBefore, out bool flush, out bool forced)
	{
		state = null;
		queueBefore = 0;
		flush = false;
		forced = false;
		if (peer?.m_socket == null || !peer.m_socket.IsConnected())
		{
			return false;
		}
		float unscaledTime = Time.unscaledTime;
		state = GetOrCreate(peer, unscaledTime);
		state.LastSeen = unscaledTime;
		try
		{
			queueBefore = Math.Max(0, peer.m_socket.GetSendQueueSize());
		}
		catch (Exception ex)
		{
			WarheimNetwork.Log.LogWarning((object)("[ZDO] Lecture de file impossible pour " + PeerLabel(peer) + " : " + ex.GetType().Name + ": " + ex.Message));
			return false;
		}
		ObserveQueue(state, queueBefore);
		SampleAndAdapt(state, unscaledTime);
		float num = Math.Max(0.1f, NetworkConfig.ZdoMaximumStarvationSeconds.Value);
		forced = unscaledTime - state.LastSend >= num;
		if (unscaledTime < state.NextEligibleSend && !forced)
		{
			state.SkippedForInterval++;
			return false;
		}
		int num2 = EffectiveHardLimit();
		if (queueBefore >= num2)
		{
			state.SkippedForQueue++;
			if (queueBefore >= EffectiveEmergencyLimit())
			{
				state.EmergencySamples++;
			}
			return false;
		}
		float val = Math.Max(0.01f, NetworkConfig.ZdoSendInterval.Value);
		state.NextEligibleSend = unscaledTime + Math.Max(val, state.SendInterval);
		state.QueueBeforeSend = queueBefore;
		state.SendAttempts++;
		if (forced)
		{
			state.ForcedSends++;
		}
		int num3 = Math.Max(10240, EffectiveSoftLimit() * Mathf.Clamp(NetworkConfig.ZdoFlushThresholdPercent.Value, 5, 90) / 100);
		flush = queueBefore <= num3;
		if (flush)
		{
			state.FlushSends++;
		}
		return true;
	}

	internal static void BeginSend(PeerState state)
	{
		_activeSend = state;
	}

	internal static void EndSend(PeerState state, int queueBefore)
	{
		try
		{
			if (state?.Peer?.m_socket != null)
			{
				int num = Math.Max(0, state.Peer.m_socket.GetSendQueueSize());
				int num2 = Math.Max(0, num - queueBefore);
				state.BytesEnqueued += num2;
				state.SuccessfulSends++;
				state.LastSend = Time.unscaledTime;
				ObserveQueue(state, num);
			}
		}
		catch
		{
		}
		finally
		{
			_activeSend = null;
		}
	}

	internal static int EffectiveQueueTarget(int configuredTarget)
	{
		if (_activeSend == null)
		{
			return Math.Max(10240, configuredTarget);
		}
		int val = (NetworkConfig.IsModuleEnabled(NetworkConfig.AdaptiveBackpressureEnabled) ? _activeSend.PackageBudget : configuredTarget);
		int num = _activeSend.QueueBeforeSend + Math.Max(10240, val);
		return Mathf.Clamp(num, 10240, EffectiveHardLimit());
	}

	internal static int EffectiveQueueGate(int configuredGate)
	{
		if (_activeSend == null)
		{
			return Math.Max(10240, configuredGate);
		}
		return EffectiveHardLimit();
	}

	internal static void Maintenance(float now)
	{
		if (now < _nextMaintenance)
		{
			return;
		}
		_nextMaintenance = now + 1f;
		List<ZNetPeer> list = null;
		foreach (KeyValuePair<ZNetPeer, PeerState> state in States)
		{
			PeerState value = state.Value;
			if (now - value.LastSeen > 10f || state.Key?.m_socket == null || !state.Key.m_socket.IsConnected())
			{
				if (list == null)
				{
					list = new List<ZNetPeer>();
				}
				list.Add(state.Key);
				LogPeerSummary(value, "retrait");
				continue;
			}
			ConfigEntry<bool> verboseLogging = NetworkConfig.VerboseLogging;
			if (verboseLogging != null && verboseLogging.Value && now >= value.NextDiagnostic)
			{
				value.NextDiagnostic = now + Math.Max(5f, NetworkConfig.PeerDiagnosticsIntervalSeconds.Value);
				LogPeerSummary(value, "suivi");
				value.PeakQueue = value.Queue;
			}
		}
		if (list == null)
		{
			return;
		}
		foreach (ZNetPeer item in list)
		{
			States.Remove(item);
		}
	}

	internal static void Reset()
	{
		States.Clear();
		_activeSend = null;
		_nextMaintenance = 0f;
	}

	private static PeerState GetOrCreate(ZNetPeer peer, float now)
	{
		if (States.TryGetValue(peer, out var value))
		{
			return value;
		}
		int packageBudget = Mathf.Clamp(NetworkConfig.ZdoQueueLimit.Value, NetworkConfig.ZdoMinimumPackageBytes.Value, NetworkConfig.ZdoMaximumPackageBytes.Value);
		value = new PeerState
		{
			Peer = peer,
			State = PeerCongestionState.Loading,
			CreatedAt = now,
			LastSeen = now,
			LastSend = now - Math.Max(0.1f, NetworkConfig.ZdoMaximumStarvationSeconds.Value),
			NextQualitySample = now,
			NextDiagnostic = now + Math.Max(5f, NetworkConfig.PeerDiagnosticsIntervalSeconds.Value),
			LastQueueSampleTime = now,
			PackageBudget = packageBudget,
			SendInterval = Math.Max(0.01f, NetworkConfig.ZdoSendInterval.Value)
		};
		States.Add(peer, value);
		return value;
	}

	private static void ObserveQueue(PeerState state, int queue)
	{
		state.Queue = Math.Max(0, queue);
		state.PeakQueue = Math.Max(state.PeakQueue, state.Queue);
		state.QueueEma = ((state.QueueEma <= 0f) ? ((float)state.Queue) : Mathf.Lerp(state.QueueEma, (float)state.Queue, 0.2f));
	}

	private static void SampleAndAdapt(PeerState state, float now)
	{
		if (now < state.NextQualitySample)
		{
			return;
		}
		state.NextQualitySample = now + 1f;
		float num = Math.Max(0.1f, now - state.LastQueueSampleTime);
		float num2 = (float)(state.Queue - state.LastQueueSample) / num;
		state.QueueSlopeEma = Mathf.Lerp(state.QueueSlopeEma, num2, 0.25f);
		state.LastQueueSample = state.Queue;
		state.LastQueueSampleTime = now;
		try
		{
			float localQuality = default(float);
			float remoteQuality = default(float);
			int num3 = default(int);
			float val = default(float);
			float val2 = default(float);
			state.Peer.m_socket.GetConnectionQuality(ref localQuality, ref remoteQuality, ref num3, ref val, ref val2);
			state.Ping = num3;
			state.Quality = NormalizeQuality(localQuality, remoteQuality);
			state.OutBytesPerSecond = Math.Max(0f, val);
			state.InBytesPerSecond = Math.Max(0f, val2);
			if (num3 > 0)
			{
				if (state.BaselinePing < 0f)
				{
					state.BaselinePing = num3;
					state.PingEma = num3;
				}
				else
				{
					state.BaselinePing = Math.Min(state.BaselinePing * 1.0025f, num3);
					float pingEma = state.PingEma;
					state.PingEma = Mathf.Lerp(state.PingEma, (float)num3, 0.15f);
					state.JitterEma = Mathf.Lerp(state.JitterEma, Math.Abs((float)num3 - pingEma), 0.2f);
				}
			}
		}
		catch
		{
			state.Ping = -1;
			state.Quality = -1f;
			state.OutBytesPerSecond = 0f;
			state.InBytesPerSecond = 0f;
		}
		float num4 = EffectiveSoftLimit();
		float num5 = EffectiveHardLimit();
		float val3 = Mathf.Clamp01((state.QueueEma - num4 * 0.35f) / Math.Max(1f, num5 - num4 * 0.35f));
		float num6 = Math.Max(32768f, state.OutBytesPerSecond);
		float num7 = Mathf.Clamp01(Math.Max(0f, state.QueueSlopeEma) / num6);
		float num8 = Math.Max(20f, state.BaselinePing * 0.2f);
		float num9 = ((state.BaselinePing > 0f) ? Mathf.Clamp01((state.PingEma - state.BaselinePing - num8) / Math.Max(50f, state.BaselinePing)) : 0f);
		float num10 = ((state.Quality < 0f) ? 0f : Mathf.Clamp01((0.85f - state.Quality) / 0.5f));
		state.Pressure = Math.Max(val3, Math.Max(num7 * 0.85f, Math.Max(num9 * 0.75f, num10 * 0.65f)));
		if (state.Queue >= EffectiveEmergencyLimit())
		{
			state.Pressure = 1f;
			state.State = PeerCongestionState.Emergency;
			state.EmergencySamples++;
		}
		else if (state.Pressure >= 0.7f)
		{
			state.State = PeerCongestionState.Congested;
		}
		else if (now - state.CreatedAt <= Math.Max(5f, NetworkConfig.ZdoCatchUpSeconds.Value))
		{
			state.State = PeerCongestionState.Loading;
		}
		else
		{
			state.State = PeerCongestionState.Open;
		}
		AdaptWindow(state);
	}

	private static void AdaptWindow(PeerState state)
	{
		int num = Math.Min(NetworkConfig.ZdoMinimumPackageBytes.Value, NetworkConfig.ZdoMaximumPackageBytes.Value);
		int num2 = Math.Max(NetworkConfig.ZdoMinimumPackageBytes.Value, NetworkConfig.ZdoMaximumPackageBytes.Value);
		float num3 = Math.Min(NetworkConfig.ZdoMinimumPeerInterval.Value, NetworkConfig.ZdoMaximumPeerInterval.Value);
		float num4 = Math.Max(NetworkConfig.ZdoMinimumPeerInterval.Value, NetworkConfig.ZdoMaximumPeerInterval.Value);
		if (!NetworkConfig.IsModuleEnabled(NetworkConfig.AdaptiveBackpressureEnabled))
		{
			state.PackageBudget = Mathf.Clamp(NetworkConfig.ZdoQueueLimit.Value, num, num2);
			state.SendInterval = Math.Max(0.01f, NetworkConfig.ZdoSendInterval.Value);
			return;
		}
		if (state.Pressure >= 0.85f)
		{
			state.PackageBudget = Mathf.FloorToInt((float)state.PackageBudget * 0.65f);
			state.SendInterval *= 1.35f;
		}
		else if (state.Pressure >= 0.55f)
		{
			state.PackageBudget = Mathf.FloorToInt((float)state.PackageBudget * 0.82f);
			state.SendInterval *= 1.15f;
		}
		else if (state.Pressure <= 0.25f)
		{
			int num5 = Math.Max(2048, Mathf.FloorToInt((float)state.PackageBudget * 0.08f));
			state.PackageBudget += num5;
			state.SendInterval -= 0.002f;
		}
		else
		{
			int num6 = Mathf.Clamp(NetworkConfig.ZdoQueueLimit.Value, num, num2);
			state.PackageBudget = Mathf.RoundToInt(Mathf.Lerp((float)state.PackageBudget, (float)num6, 0.05f));
			state.SendInterval = Mathf.Lerp(state.SendInterval, NetworkConfig.ZdoSendInterval.Value, 0.05f);
		}
		state.PackageBudget = Mathf.Clamp(state.PackageBudget, num, num2);
		state.SendInterval = Mathf.Clamp(state.SendInterval, num3, num4);
	}

	private static int EffectiveSoftLimit()
	{
		return Math.Max(10240, NetworkConfig.ZdoQueueSoftLimit.Value);
	}

	private static int EffectiveHardLimit()
	{
		return Math.Max(EffectiveSoftLimit() + 10240, NetworkConfig.ZdoQueueHardLimit.Value);
	}

	private static int EffectiveEmergencyLimit()
	{
		return Math.Max(EffectiveHardLimit() + 10240, NetworkConfig.ZdoQueueEmergencyLimit.Value);
	}

	private static float NormalizeQuality(float localQuality, float remoteQuality)
	{
		float num = NormalizeQualityValue(localQuality);
		float num2 = NormalizeQualityValue(remoteQuality);
		if (num < 0f)
		{
			return num2;
		}
		if (num2 < 0f)
		{
			return num;
		}
		return Math.Min(num, num2);
	}

	private static float NormalizeQualityValue(float value)
	{
		if (value < 0f)
		{
			return -1f;
		}
		return (value > 1f) ? Mathf.Clamp01(value / 100f) : Mathf.Clamp01(value);
	}

	private static void LogPeerSummary(PeerState state, string reason)
	{
		if (state?.Peer != null)
		{
			WarheimNetwork.Log.LogInfo((object)($"[Peer] {reason} {PeerLabel(state.Peer)} | état={state.State}, pression={state.Pressure:P0}, " + $"queue={state.Queue}/{state.PeakQueue}, pente={state.QueueSlopeEma / 1024f:F0} Ko/s, " + $"rtt={state.Ping} ms, base={state.BaselinePing:F0}, jitter={state.JitterEma:F0}, qualité={FormatQuality(state.Quality)}, " + $"débit={state.OutBytesPerSecond / 1024f:F0}/{state.InBytesPerSecond / 1024f:F0} Ko/s, " + $"budget={state.PackageBudget}, intervalle={state.SendInterval:F3}s, envois={state.SuccessfulSends}, " + $"flush={state.FlushSends}, forcés={state.ForcedSends}, skipQueue={state.SkippedForQueue}, " + $"skipInterval={state.SkippedForInterval}, injecté={(double)state.BytesEnqueued / 1048576.0:F1} Mio."));
		}
	}

	private static string PeerLabel(ZNetPeer peer)
	{
		string arg = (string.IsNullOrWhiteSpace(peer?.m_playerName) ? "peer" : peer.m_playerName);
		return $"{arg}/{peer?.m_uid ?? 0}";
	}

	private static string FormatQuality(float quality)
	{
		return (quality < 0f) ? "n/a" : $"{quality * 100f:F0}%";
	}
}
internal static class SavePressureGuard
{
	private struct SaveRecord
	{
		internal int Prefab;

		internal Vector2s Sector;

		internal Vector3 Rotation;

		internal Vector3 Position;

		internal bool Persistent;

		internal bool Distant;

		internal ObjectType Type;

		internal ZDOConnectionHashData Connection;

		internal KeyValuePair<int, float>[] Floats;

		internal int FloatCount;

		internal KeyValuePair<int, Vector3>[] Vec3;

		internal int Vec3Count;

		internal KeyValuePair<int, Quaternion>[] Quats;

		internal int QuatCount;

		internal KeyValuePair<int, int>[] Ints;

		internal int IntCount;

		internal KeyValuePair<int, long>[] Longs;

		internal int LongCount;

		internal KeyValuePair<int, string>[] Strings;

		internal int StringCount;

		internal KeyValuePair<int, byte[]>[] ByteArrays;

		internal int ByteArrayCount;
	}

	private static class BlockAccessor<T>
	{
		private static KeyValuePair<int, T>[] _buffer = Array.Empty<KeyValuePair<int, T>>();

		internal static bool Validate()
		{
			return typeof(ICollection<KeyValuePair<int, T>>).IsAssignableFrom(typeof(BinarySearchDictionary<int, T>));
		}

		internal static KeyValuePair<int, T>[] Rent(int count)
		{
			KeyValuePair<int, T>[] buffer = _buffer;
			if (buffer.Length >= count)
			{
				return buffer;
			}
			int num = ((buffer.Length == 0) ? 4 : buffer.Length);
			while (num < count)
			{
				int num2 = ((num < 1024) ? (num * 2) : (num + num / 2));
				if (num2 <= num)
				{
					num = count;
					break;
				}
				num = num2;
			}
			return _buffer = new KeyValuePair<int, T>[num];
		}
	}

	private static class IlReader
	{
		internal static readonly OpCode[] OneByte;

		internal static readonly OpCode[] TwoByte;

		static IlReader()
		{
			OneByte = new OpCode[256];
			TwoByte = new OpCode[256];
			FieldInfo[] fields = typeof(OpCodes).GetFields(BindingFlags.Static | BindingFlags.Public);
			for (int i = 0; i < fields.Length; i++)
			{
				if (fields[i].GetValue(null) is OpCode opCode)
				{
					ushort num = (ushort)opCode.Value;
					if (num < 256)
					{
						OneByte[num] = opCode;
					}
					else if ((num & 0xFF00) == 65024)
					{
						TwoByte[num & 0xFF] = opCode;
					}
				}
			}
		}
	}

	private static readonly MethodInfo RegenerateConnectionsMethod = AccessTools.Method(typeof(ZDOExtraData), "RegenerateConnectionHashData", (Type[])null, (Type[])null);

	private static readonly FieldInfo FloatsField = AccessTools.Field(typeof(ZDOExtraData), "s_floats");

	private static readonly FieldInfo Vec3Field = AccessTools.Field(typeof(ZDOExtraData), "s_vec3");

	private static readonly FieldInfo QuatsField = AccessTools.Field(typeof(ZDOExtraData), "s_quats");

	private static readonly FieldInfo IntsField = AccessTools.Field(typeof(ZDOExtraData), "s_ints");

	private static readonly FieldInfo LongsField = AccessTools.Field(typeof(ZDOExtraData), "s_longs");

	private static readonly FieldInfo StringsField = AccessTools.Field(typeof(ZDOExtraData), "s_strings");

	private static readonly FieldInfo ByteArraysField = AccessTools.Field(typeof(ZDOExtraData), "s_byteArrays");

	private static readonly FieldInfo ConnectionsField = AccessTools.Field(typeof(ZDOExtraData), "s_connectionsHashData");

	private static readonly FieldInfo SaveFloatsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveFloats");

	private static readonly FieldInfo SaveVec3Field = AccessTools.Field(typeof(ZDOExtraData), "s_saveVec3s");

	private static readonly FieldInfo SaveQuatsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveQuats");

	private static readonly FieldInfo SaveIntsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveInts");

	private static readonly FieldInfo SaveLongsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveLongs");

	private static readonly FieldInfo SaveStringsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveStrings");

	private static readonly FieldInfo SaveByteArraysField = AccessTools.Field(typeof(ZDOExtraData), "s_saveByteArrays");

	private static readonly FieldInfo SaveConnectionsField = AccessTools.Field(typeof(ZDOExtraData), "s_saveConnections");

	private static FieldRef<ZDO, int> _zdoPrefab;

	private static FieldRef<ZDO, Vector2s> _zdoSector;

	private static FieldRef<ZDO, Vector3> _zdoRotation;

	private static FieldRef<ZDO, Vector3> _zdoPosition;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, float>> _snapshotFloats;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>> _snapshotVec3;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>> _snapshotQuats;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, int>> _snapshotInts;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, long>> _snapshotLongs;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, string>> _snapshotStrings;

	private static Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>> _snapshotByteArrays;

	private static Dictionary<ZDOID, ZDOConnectionHashData> _snapshotConnections;

	private static bool _installed;

	private static bool _guardReady;

	private static bool _snapshotFieldsReady;

	private static bool _serializerReady;

	private static bool _compatibilityAudited;

	private static int _barrier;

	private static int _snapshotBound;

	private static int _adminSaveDepth;

	private static float _barrierStartedAt;

	private static long _lastErrorLogTicks;

	private static long _barrierStartManagedBytes;

	private static int _barrierStartZdos;

	private static string _saveOrigin = "inconnue";

	internal static bool IsActive => Volatile.Read(in _barrier) != 0;

	internal static void Install(Harmony harmony)
	{
		//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Expected O, but got Unknown
		//IL_0215: Expected O, but got Unknown
		//IL_0228: 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_024a: Expected O, but got Unknown
		//IL_024a: Expected O, but got Unknown
		//IL_025d: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Expected O, but got Unknown
		//IL_027f: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Expected O, but got Unknown
		//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ae: Expected O, but got Unknown
		//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f3: Expected O, but got Unknown
		//IL_02f3: Expected O, but got Unknown
		if (_installed)
		{
			return;
		}
		_installed = true;
		_snapshotFieldsReady = ValidateSnapshotFields();
		MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "Save", new Type[3]
		{
			typeof(bool),
			typeof(bool),
			typeof(bool)
		}, (Type[])null);
		MethodInfo methodInfo2 = AccessTools.Method(typeof(ZNet), "SaveWorld", new Type[1] { typeof(bool) }, (Type[])null);
		MethodInfo methodInfo3 = AccessTools.Method(typeof(ZDOExtraData), "PrepareSave", (Type[])null, (Type[])null);
		MethodInfo methodInfo4 = AccessTools.Method(typeof(ZNet), "Update", (Type[])null, (Type[])null);
		MethodInfo methodInfo5 = AccessTools.Method(typeof(ZDO), "Save", new Type[1] { typeof(ZPackage) }, (Type[])null);
		MethodInfo methodInfo6 = AccessTools.Method(typeof(ZNet), "RPC_Save", new Type[1] { typeof(ZRpc) }, (Type[])null);
		if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || methodInfo4 == null || methodInfo5 == null)
		{
			WarheimNetwork.Log.LogError((object)($"[SaveGuard] Installation refusée : Save={methodInfo != null}, SaveWorld={methodInfo2 != null}, " + $"PrepareSave={methodInfo3 != null}, Update={methodInfo4 != null}, ZDO.Save={methodInfo5 != null}."));
			return;
		}
		_serializerReady = _snapshotFieldsReady && ValidateSerializerContract(methodInfo5);
		if (_serializerReady)
		{
			WarheimNetwork.Log.LogInfo((object)"[SaveGuard] BinarySearchDictionary validé via API publique Count/CopyTo, sans dépendance aux champs privés.");
		}
		try
		{
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(SavePressureGuard), "SavePrefix", (Type[])null), new HarmonyMethod(typeof(SavePressureGuard), "SavePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(SavePressureGuard), "SaveWorldPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SavePressureGuard), "SaveWorldFinalizer", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(SavePressureGuard), "PrepareSavePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(SavePressureGuard), "ZdoSavePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(typeof(SavePressureGuard), "ZNetUpdatePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			if (methodInfo6 != null)
			{
				harmony.Patch((MethodBase)methodInfo6, new HarmonyMethod(typeof(SavePressureGuard), "RpcSavePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(SavePressureGuard), "RpcSaveFinalizer", (Type[])null), (HarmonyMethod)null);
			}
			_guardReady = true;
			WarheimNetwork.Log.LogInfo((object)$"[SaveGuard] Protection installée. Snapshot={_snapshotFieldsReady}, sérialiseur ZDO sans listes={_serializerReady}, origine admin={methodInfo6 != null}.");
		}
		catch (Exception ex)
		{
			WarheimNetwork.Log.LogError((object)("[SaveGuard] Installation impossible : " + ex.GetType().Name + ": " + ex.Message));
		}
	}

	internal static void Reset()
	{
		Interlocked.Exchange(ref _barrier, 0);
		Interlocked.Exchange(ref _snapshotBound, 0);
		Interlocked.Exchange(ref _adminSaveDepth, 0);
		_barrierStartedAt = 0f;
		_barrierStartManagedBytes = 0L;
		_barrierStartZdos = 0;
		_saveOrigin = "inconnue";
		ClearSnapshotReferences(clearGameSnapshotFields: false);
	}

	private static void RpcSavePrefix()
	{
		Interlocked.Increment(ref _adminSaveDepth);
		if (ShouldProtectSave())
		{
			_saveOrigin = "commande admin vanilla";
			EnterBarrier("commande admin vanilla / sauvegarde des profils");
		}
	}

	private static Exception RpcSaveFinalizer(Exception __exception)
	{
		if (Interlocked.Decrement(ref _adminSaveDepth) < 0)
		{
			Interlocked.Exchange(ref _adminSaveDepth, 0);
		}
		if (IsActive && ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsSaving()))
		{
			ExitBarrier("fin RPC_Save");
		}
		return __exception;
	}

	private static void SavePrefix(bool __2, out bool __state)
	{
		__state = ShouldProtectSave();
		if (__state)
		{
			_saveOrigin = ((Volatile.Read(in _adminSaveDepth) > 0) ? "commande admin vanilla" : (__2 ? "autosave ou sauvegarde différée" : "sauvegarde système immédiate"));
			EnterBarrier(_saveOrigin);
			NetworkDiagnostics.RecordSaveRequest(_saveOrigin);
		}
	}

	private static void SavePostfix(bool __2, bool __state)
	{
		if (__state && !__2)
		{
			ExitBarrier("retour Save immédiat");
		}
	}

	private static void SaveWorldPrefix(ref bool __0)
	{
		if (ShouldProtectSave())
		{
			EnterBarrier("SaveWorld/" + _saveOrigin);
			if (!__0)
			{
				__0 = true;
				NetworkDiagnostics.RecordForcedSynchronousSave();
			}
		}
	}

	private static Exception SaveWorldFinalizer(Exception __exception)
	{
		ClearSnapshotReferences(__exception != null);
		ExitBarrier("fin SaveWorld");
		return __exception;
	}

	private static bool PrepareSavePrefix()
	{
		Interlocked.Exchange(ref _snapshotBound, 0);
		ClearSnapshotReferences(clearGameSnapshotFields: false);
		if (!ShouldProtectSave() || !_snapshotFieldsReady)
		{
			return true;
		}
		try
		{
			RegenerateConnectionsMethod.Invoke(null, null);
			Dictionary<ZDOID, BinarySearchDictionary<int, float>> dictionary = (Dictionary<ZDOID, BinarySearchDictionary<int, float>>)FloatsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>> dictionary2 = (Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>>)Vec3Field.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>> dictionary3 = (Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>>)QuatsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, int>> dictionary4 = (Dictionary<ZDOID, BinarySearchDictionary<int, int>>)IntsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, long>> dictionary5 = (Dictionary<ZDOID, BinarySearchDictionary<int, long>>)LongsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, string>> dictionary6 = (Dictionary<ZDOID, BinarySearchDictionary<int, string>>)StringsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>> dictionary7 = (Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>>)ByteArraysField.GetValue(null);
			Dictionary<ZDOID, ZDOConnectionHashData> dictionary8 = (Dictionary<ZDOID, ZDOConnectionHashData>)ConnectionsField.GetValue(null);
			Dictionary<ZDOID, BinarySearchDictionary<int, float>> dictionary9 = new Dictionary<ZDOID, BinarySearchDictionary<int, float>>(dictionary);
			Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>> dictionary10 = new Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>>(dictionary2);
			Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>> dictionary11 = new Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>>(dictionary3);
			Dictionary<ZDOID, BinarySearchDictionary<int, int>> dictionary12 = new Dictionary<ZDOID, BinarySearchDictionary<int, int>>(dictionary4);
			Dictionary<ZDOID, BinarySearchDictionary<int, long>> dictionary13 = new Dictionary<ZDOID, BinarySearchDictionary<int, long>>(dictionary5);
			Dictionary<ZDOID, BinarySearchDictionary<int, string>> dictionary14 = new Dictionary<ZDOID, BinarySearchDictionary<int, string>>(dictionary6);
			Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>> dictionary15 = new Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>>(dictionary7);
			Dictionary<ZDOID, ZDOConnectionHashData> dictionary16 = new Dictionary<ZDOID, ZDOConnectionHashData>(dictionary8);
			SaveFloatsField.SetValue(null, dictionary9);
			SaveVec3Field.SetValue(null, dictionary10);
			SaveQuatsField.SetValue(null, dictionary11);
			SaveIntsField.SetValue(null, dictionary12);
			SaveLongsField.SetValue(null, dictionary13);
			SaveStringsField.SetValue(null, dictionary14);
			SaveByteArraysField.SetValue(null, dictionary15);
			SaveConnectionsField.SetValue(null, dictionary16);
			long avoidedInnerClones = (long)dictionary9.Count + (long)dictionary10.Count + dictionary11.Count + dictionary12.Count + dictionary13.Count + dictionary14.Count + dictionary15.Count + dictionary16.Count;
			NetworkDiagnostics.RecordLowAllocationSaveSnapshot(avoidedInnerClones);
			if (_serializerReady)
			{
				_snapshotFloats = dictionary9;
				_snapshotVec3 = dictionary10;
				_snapshotQuats = dictionary11;
				_snapshotInts = dictionary12;
				_snapshotLongs = dictionary13;
				_snapshotStrings = dictionary14;
				_snapshotByteArrays = dictionary15;
				_snapshotConnections = dictionary16;
				Volatile.Write(ref _snapshotBound, 1);
			}
			return false;
		}
		catch (Exception ex)
		{
			Interlocked.Exchange(ref _snapshotBound, 0);
			ClearSnapshotReferences(clearGameSnapshotFields: true);
			LogFailure("snapshot de sauvegarde", ex);
			return true;
		}
	}

	private static bool ZdoSavePrefix(ZDO __instance, ZPackage pkg)
	{
		if (!ShouldUseFastSerializer() || __instance == null || pkg == null)
		{
			return true;
		}
		if (!TryCaptureRecord(__instance, out var record))
		{
			return true;
		}
		WriteRecord(pkg, ref record);
		NetworkDiagnostics.RecordFastSerializedZdo(record.FloatCount + record.Vec3Count + record.QuatCount + record.IntCount + record.LongCount + record.StringCount + record.ByteArrayCount);
		return false;
	}

	private static bool TryCaptureRecord(ZDO zdo, out SaveRecord record)
	{
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0075: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		record = default(SaveRecord);
		try
		{
			Dictionary<ZDOID, BinarySearchDictionary<int, float>> snapshotFloats = _snapshotFloats;
			Dictionary<ZDOID, BinarySearchDictionary<int, Vector3>> snapshotVec = _snapshotVec3;
			Dictionary<ZDOID, BinarySearchDictionary<int, Quaternion>> snapshotQuats = _snapshotQuats;
			Dictionary<ZDOID, BinarySearchDictionary<int, int>> snapshotInts = _snapshotInts;
			Dictionary<ZDOID, BinarySearchDictionary<int, long>> snapshotLongs = _snapshotLongs;
			Dictionary<ZDOID, BinarySearchDictionary<int, string>> snapshotStrings = _snapshotStrings;
			Dictionary<ZDOID, BinarySearchDictionary<int, byte[]>> snapshotByteArrays = _snapshotByteArrays;
			Dictionary<ZDOID, ZDOConnectionHashData> snapshotConnections = _snapshotConnections;
			if (snapshotFloats == null || snapshotVec == null || snapshotQuats == null || snapshotInts == null || snapshotLongs == null || snapshotStrings == null || snapshotByteArrays == null || snapshotConnections == null)
			{
				return false;
			}
			ZDOID uid = zdo.m_uid;
			snapshotFloats.TryGetValue(uid, out var value);
			snapshotVec.TryGetValue(uid, out var value2);
			snapshotQuats.TryGetValue(uid, out var value3);
			snapshotInts.TryGetValue(uid, out var value4);
			snapshotLongs.TryGetValue(uid, out var value5);
			snapshotStrings.TryGetValue(uid, out var value6);
			snapshotByteArrays.TryGetValue(uid, out var value7);
			snapshotConnections.TryGetValue(uid, out var value8);
			record.Prefab = _zdoPrefab.Invoke(zdo);
			record.Sector = _zdoSector.Invoke(zdo);
			record.Rotation = _zdoRotation.Invoke(zdo);
			record.Position = _zdoPosition.Invoke(zdo);
			record.Persistent = zdo.Persistent;
			record.Distant = zdo.Distant;
			record.Type = zdo.Type;
			record.Connection = value8;
			CaptureBlock(value, out record.Floats, out record.FloatCount);
			CaptureBlock(value2, out record.Vec3, out record.Vec3Count);
			CaptureBlock(value3, out record.Quats, out record.QuatCount);
			CaptureBlock(value4, out record.Ints, out record.IntCount);
			CaptureBlock(value5, out record.Longs, out record.LongCount);
			CaptureBlock(value6, out record.Strings, out record.StringCount);
			CaptureBlock(value7, out record.ByteArrays, out record.ByteArrayCount);
			return true;
		}
		catch (Exception ex)
		{
			DisableFastSerializer(ex);
			record = default(SaveRecord);
			return false;
		}
	}

	private static void WriteRecord(ZPackage pkg, ref SaveRecord record)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Invalid comparison between Unknown and I4
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: 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_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: 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_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Expected I4, but got Unknown
		ushort num = 0;
		if (record.Connection != null && (int)record.Connection.m_type > 0)
		{
			num |= 1;
		}
		if (record.FloatCount > 0)
		{
			num |= 2;
		}
		if (record.Vec3Count > 0)
		{
			num |= 4;
		}
		if (record.QuatCount > 0)
		{
			num |= 8;
		}
		if (record.IntCount > 0)
		{
			num |= 0x10;
		}
		if (record.LongCount > 0)
		{
			num |= 0x20;
		}
		if (record.StringCount > 0)
		{
			num |= 0x40;
		}
		if (record.ByteArrayCount > 0)
		{
			num |= 0x80;
		}
		bool flag = record.Rotation != Vector3.zero;
		num |= (ushort)(record.Persistent ? 256 : 0);
		num |= (ushort)(record.Distant ? 512 : 0);
		num |= (ushort)(record.Type << 10);
		num |= (ushort)(flag ? 4096 : 0);
		pkg.Write(num);
		pkg.Write(record.Sector);
		pkg.Write(record.Position);
		pkg.Write(record.Prefab);
		if (flag)
		{
			pkg.Write(record.Rotation);
		}
		if ((num & 0xFF) != 0)
		{
			if ((num & 1) != 0)
			{
				pkg.Write((byte)(int)record.Connection.m_type);
				pkg.Write(record.Connection.m_hash);
			}
			WriteFloatData(pkg, record.Floats, record.FloatCount);
			WriteVec3Data(pkg, record.Vec3, record.Vec3Count);
			WriteQuatData(pkg, record.Quats, record.QuatCount);
			WriteIntData(pkg, record.Ints, record.IntCount);
			WriteLongData(pkg, record.Longs, record.LongCount);
			WriteStringData(pkg, record.Strings, record.StringCount);
			WriteByteArrayData(pkg, record.ByteArrays, record.ByteArrayCount);
		}
	}

	private static void WriteFloatData(ZPackage pkg, KeyValuePair<int, float>[] data, int count)
	{
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
			}
		}
	}

	private static void WriteVec3Data(ZPackage pkg, KeyValuePair<int, Vector3>[] data, int count)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
			}
		}
	}

	private static void WriteQuatData(ZPackage pkg, KeyValuePair<int, Quaternion>[] data, int count)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
			}
		}
	}

	private static void WriteIntData(ZPackage pkg, KeyValuePair<int, int>[] data, int count)
	{
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
			}
		}
	}

	private static void WriteLongData(ZPackage pkg, KeyValuePair<int, long>[] data, int count)
	{
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
			}
		}
	}

	private static void WriteStringData(ZPackage pkg, KeyValuePair<int, string>[] data, int count)
	{
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
				data[i] = default(KeyValuePair<int, string>);
			}
		}
	}

	private static void WriteByteArrayData(ZPackage pkg, KeyValuePair<int, byte[]>[] data, int count)
	{
		if (count > 0)
		{
			pkg.WriteNumItems(count);
			for (int i = 0; i < count; i++)
			{
				pkg.Write(data[i].Key);
				pkg.Write(data[i].Value);
				data[i] = default(KeyValuePair<int, byte[]>);
			}
		}
	}

	private static void CaptureBlock<T>(BinarySearchDictionary<int, T> dictionary, out KeyValuePair<int, T>[] block, out int count)
	{
		if (dictionary == null)
		{
			block = null;
			count = 0;
			return;
		}
		count = dictionary.Count;
		if (count <= 0)
		{
			block = null;
			count = 0;
		}
		else
		{
			block = BlockAccessor<T>.Rent(count);
			dictionary.CopyTo(block, 0);
		}
	}

	internal static void FinalizeCompatibilityAudit()
	{
		if (_compatibilityAudited)
		{
			return;
		}
		_compatibilityAudited = true;
		if (_serializerReady)
		{
			string foreignPatchOwners = GetForeignPatchOwners(AccessTools.Method(typeof(ZDO), "Save", new Type[1] { typeof(ZPackage) }, (Type[])null));
			string foreignPatchOwners2 = GetForeignPatchOwners(AccessTools.Method(typeof(ZDOExtraData), "PrepareSave", (Type[])null, (Type[])null));
			if (foreignPatchOwners.Length == 0 && foreignPatchOwners2.Length == 0)
			{
				WarheimNetwork.Log.LogInfo((object)"[SaveGuard] Compatibilité finale validée : sérialiseur ZDO sans listes autorisé.");
				return;
			}
			_serializerReady = false;
			WarheimNetwork.Log.LogWarning((object)("[SaveGuard] Sérialiseur ZDO sans listes désactivé par sécurité. Patchs étrangers ZDO.Save=[" + foreignPatchOwners + "], ZDOExtraData.PrepareSave=[" + foreignPatchOwners2 + "]. La barrière et le snapshot faible allocation restent actifs."));
		}
	}

	private static string GetForeignPatchOwners(MethodBase method)
	{
		if (method == null)
		{
			return "méthode introuvable";
		}
		Patches patchInfo = Harmony.GetPatchInfo(method);
		if (patchInfo == null)
		{
			return string.Empty;
		}
		HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		AddForeignOwners(hashSet, patchInfo.Prefixes);
		AddForeignOwners(hashSet, patchInfo.Postfixes);
		AddForeignOwners(hashSet, patchInfo.Transpilers);
		AddForeignOwners(hashSet, patchInfo.Finalizers);
		return (hashSet.Count == 0) ? string.Empty : string.Join(", ", hashSet);
	}

	private static void AddForeignOwners(HashSet<string> owners, IEnumerable<Patch> patches)
	{
		foreach (Patch patch in patches)
		{
			if (!string.IsNullOrEmpty(patch.owner) && !string.Equals(patch.owner, "dzk.warheimnetwork", StringComparison.OrdinalIgnoreCase))
			{
				owners.Add(patch.owner);
			}
		}
	}

	private static void ZNetUpdatePostfix(ZNet __instance)
	{
		if (IsActive && !(Time.unscaledTime - _barrierStartedAt < 5f) && ((Object)(object)__instance == (Object)null || !__instance.IsSaving()))
		{
			ClearSnapshotReferences(clearGameSnapshotFields: true);
			ExitBarrier("watchdog");
		}
	}

	private static bool ShouldProtectSave()
	{
		return _guardReady && Application.isBatchMode && NetworkConfig.IsModuleEnabled(NetworkConfig.SaveGuardEnabled);
	}

	private static bool ShouldUseFastSerializer()
	{
		return _guardReady && _serializerReady && IsActive && Volatile.Read(in _snapshotBound) != 0;
	}

	private static void EnterBarrier(string reason)
	{
		if (Interlocked.Exchange(ref _barrier, 1) == 0)
		{
			_barrierStartedAt = Time.unscaledTime;
			_barrierStartManagedBytes = GC.GetTotalMemory(forceFullCollection: false);
			_barrierStartZdos = ((ZDOMan.instance != null) ? ZDOMan.instance.NrOfObjects() : 0);
			TransportCompression.ReleaseWorkingBuffer();
			NetworkDiagnostics.RecordSaveBarrier();
			WarheimNetwork.Log.LogInfo((object)$"[SaveGuard] Barrière réseau activée ({reason}) | ZDO={_barrierStartZdos}, heap géré={FormatMiB(_barrierStartManagedBytes)} MiB.");
		}
	}

	private static void ExitBarrier(string reason)
	{
		if (Interlocked.Exchange(ref _barrier, 0) != 0)
		{
			float num = Math.Max(0f, Time.unscaledTime - _barrierStartedAt);
			long totalMemory = GC.GetTotalMemory(forceFullCollection: false);
			long bytes = totalMemory - _barrierStartManagedBytes;
			_barrierStartedAt = 0f;
			_barrierStartManagedBytes = 0L;
			_barrierStartZdos = 0;
			_saveOrigin = "inconnue";
			WarheimNetwork.Log.LogInfo((object)$"[SaveGuard] Barrière réseau levée ({reason}, {num:F2}s) | heap géré={FormatMiB(totalMemory)} MiB, delta={FormatSignedMiB(bytes)} MiB.");
		}
	}

	private static string FormatMiB(long bytes)
	{
		return ((double)bytes / 1048576.0).ToString("F1", CultureInfo.InvariantCulture);
	}

	private static string FormatSignedMiB(long bytes)
	{
		return ((double)bytes / 1048576.0).ToString("+0.0;-0.0;0.0", CultureInfo.InvariantCulture);
	}

	private static void ClearSnapshotReferences(bool clearGameSnapshotFields)
	{
		Interlocked.Exchange(ref _snapshotBound, 0);
		_snapshotFloats = null;
		_snapshotVec3 = null;
		_snapshotQuats = null;
		_snapshotInts = null;
		_snapshotLongs = null;
		_snapshotStrings = null;
		_snapshotByteArrays = null;
		_snapshotConnections = null;
		if (!clearGameSnapshotFields || !_snapshotFieldsReady)
		{
			return;
		}
		try
		{
			SaveFloatsField.SetValue(null, null);
			SaveVec3Field.SetValue(null, null);
			SaveQuatsField.SetValue(null, null);
			SaveIntsField.SetValue(null, null);
			SaveLongsField.SetValue(null, null);
			SaveStringsField.SetValue(null, null);
			SaveByteArraysField.SetValue(null, null);
			SaveConnectionsField.SetValue(null, null);
		}
		catch (Exception ex)
		{
			LogFailure("nettoyage du snapshot", ex);
		}
	}

	private static bool ValidateSnapshotFields()
	{
		FieldInfo[] array = new FieldInfo[16]
		{
			FloatsField, Vec3Field, QuatsField, IntsField, LongsField, StringsField, ByteArraysField, ConnectionsField, SaveFloatsField, SaveVec3Field,
			SaveQuatsField, SaveIntsField, SaveLongsField, SaveStringsField, SaveByteArraysField, SaveConnectionsField
		};
		if (RegenerateConnectionsMethod == null)
		{
			WarheimNetwork.Log.LogError((object)"[SaveGuard] RegenerateConnectionHashData introuvable. Snapshot vanilla conservé.");
			return false;
		}
		for (int i = 0; i < array.Length; i++)
		{
			if (array[i] == null)
			{
				WarheimNetwork.Log.LogError((object)"[SaveGuard] Structure ZDOExtraData incompatible. Snapshot vanilla conservé.");
				return false;
			}
		}
		return true;
	}

	private static bool ValidateSerializerContract(MethodInfo zdoSave)
	{
		try
		{
			_zdoPrefab = AccessTools.FieldRefAccess<ZDO, int>("m_prefab");
			_zdoSector = AccessTools.FieldRefAccess<ZDO, Vector2s>("m_sector");
			_zdoRotation = AccessTools.FieldRefAccess<ZDO, Vector3>("m_rotation");
			_zdoPosition = AccessTools.FieldRefAccess<ZDO, Vector3>("m_position");
			if (!BlockAccessor<float>.Validate() || !BlockAccessor<Vector3>.Validate() || !BlockAccessor<Quaternion>.Validate() || !BlockAccessor<int>.Validate() || !BlockAccessor<long>.Validate() || !BlockAccessor<string>.Validate() || !BlockAccessor<byte[]>.Validate())
			{
				WarheimNetwork.Log.LogError((object)"[SaveGuard] Structure BinarySearchDictionary incompatible. Sérialiseur vanilla conservé.");
				return false;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { "ZDOExtraData.GetSaveFloats", "ZDOExtraData.GetSaveVec3s", "ZDOExtraData.GetSaveQuaternions", "ZDOExtraData.GetSaveInts", "ZDOExtraData.GetSaveLongs", "ZDOExtraData.GetSaveStrings", "ZDOExtraData.GetSaveByteArrays", "ZDOExtraData.GetSaveConnections", "ZDODataHelper.WriteData" };
			HashSet<string> hashSet2 = ReadCalledMethods(zdoSave);
			foreach (string item in hashSet)
			{
				if (!hashSet2.Contains(item))
				{
					WarheimNetwork.Log.LogError((object)("[SaveGuard] Contrat ZDO.Save incompatible : appel manquant " + item + ". Sérialiseur vanilla conservé."));
					return false;
				}
			}
			return true;
		}
		catch (Exception ex)
		{
			LogFailure("validation du sérialiseur ZDO", ex);
			return false;
		}
	}

	private static HashSet<string> ReadCalledMethods(MethodInfo method)
	{
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
		byte[] array = method.GetMethodBody()?.GetILAsByteArray();
		if (array == null || array.Length == 0)
		{
			return hashSet;
		}
		int i = 0;
		Module module = method.Module;
		OpCode opCode;
		for (; i < array.Length; i += OperandSize(opCode.OperandType, array, i))
		{
			byte b = array[i++];
			opCode = ((b != 254) ? IlReader.OneByte[b] : IlReader.TwoByte[array[i++]]);
			if (opCode.OperandType != OperandType.InlineMethod)
			{
				continue;
			}
			int metadataToken = BitConverter.ToInt32(array, i);
			try
			{
				MethodBase methodBase = module.ResolveMethod(metadataToken);
				if (methodBase?.DeclaringType != null)
				{
					hashSet.Add(methodBase.DeclaringType.Name + "." + methodBase.Name);
				}
			}
			catch
			{
			}
		}
		return hashSet;
	}

	private static int OperandSize(OperandType operandType, byte[] il, int position)
	{
		switch (operandType)
		{
		case OperandType.InlineNone:
			return 0;
		case OperandType.ShortInlineBrTarget:
		case OperandType.ShortInlineI:
		case OperandType.ShortInlineVar:
			return 1;
		case OperandType.InlineVar:
			return 2;
		case OperandType.InlineI8:
		case OperandType.InlineR:
			return 8;
		case OperandType.InlineSwitch:
		{
			int num = BitConverter.ToInt32(il, position);
			return 4 + num * 4;
		}
		default:
			return 4;
		}
	}

	private static void DisableFastSerializer(Exception ex)
	{
		_serializerReady = false;
		Interlocked.Exchange(ref _snapshotBound, 0);
		LogFailure("sérialiseur ZDO sans listes, désactivé pour la session", ex);
	}

	private static void LogFailure(string operation, Exception ex)
	{
		long ticks = DateTime.UtcNow.Ticks;
		long num = Interlocked.Read(in _lastErrorLogTicks);
		if (num == 0L || ticks - num >= 100000000)
		{
			Interlocked.Exchange(ref _lastErrorLogTicks, ticks);
			Exception ex2 = ((ex is TargetInvocationException && ex.InnerException != null) ? ex.InnerException : ex);
			WarheimNetwork.Log.LogError((object)("[SaveGuard] Échec " + operation + ". Retour de sécurité : " + ex2.GetType().Name + ": " + ex2.Message));
		}
	}
}
[HarmonyPatch]
internal static class ShipSyncPatches
{
	private const float RudderSendInterval = 0.05f;

	private const float AttachedPositionSmoothTime = 0.08f;

	private const float AttachedRotationSmoothTime = 0.1f;

	private const float AttachedSnapDistance = 5f;

	private static readonly FieldInfo SendRudderTimeField = AccessTools.Field(typeof(Ship), "m_sendRudderTime");

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

	private static float _lastWarningTime = -999f;

	internal static void Validate()
	{
		if (SendRudderTimeField == null)
		{
			WarheimNetwork.Log.LogError((object)"[ShipSync] Ship.m_sendRudderTime introuvable. L'envoi du gouvernail à 20 Hz est désactivé.");
		}
		else
		{
			WarheimNetwork.Log.LogInfo((object)"[ShipSync] Gouvernail 20 Hz, interpolation Rigidbody et lissage des joueurs attachés installés.");
		}
	}

	internal static void Reset()
	{
		PlayerVelocities.Clear();
	}

	[HarmonyPatch(typeof(Ship), "ApplyControlls")]
	[HarmonyPostfix]
	private static void FasterRudderPostfix(Ship __instance)
	{
		if (!NetworkConfig.IsModuleEnabled(NetworkConfig.ShipSyncEnabled) || SendRudderTimeField == null || (Object)(object)__instance == (Object)null || (Object)(object)__instance.m_nview == (Object)null || !__instance.m_nview.IsValid() || !__instance.m_nview.IsOwner())
		{
			return;
		}
		try
		{
			float num = (float)SendRudderTimeField.GetValue(__instance);
			float time = Time.time;
			if (!(time - num < 0.05f))
			{
				SendRudderTimeField.SetValue(__instance, time);
				__instance.m_nview.InvokeRPC("Rudder", new object[1] { __instance.m_rudderValue });
				NetworkDiagnostics.RecordShipRudderSend();
			}
		}
		catch (Exception ex)
		{
			if (Time.unscaledTime - _lastWarningTime >= 10f)
			{
				_lastWarningTime = Time.unscaledTime;
				WarheimNetwork.Log.LogWarning((object)("[ShipSync] Envoi du gouvernail impossible : " + ex.GetType().Name + ": " + ex.Message));
			}
		}
	}

	[HarmonyPatch(typeof(Ship), "Awake")]
	[HarmonyPostfix]
	private static void EnableShipInterpolationPostfix(Ship __instance)
	{
		if (NetworkConfig.IsModuleEnabled(NetworkConfig.ShipSyncEnabled) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance.m_body == (Object)null))
		{
			__instance.m_body.interpolation = (RigidbodyInterpolation)1;
			if (!__instance.m_body.isKinematic)
			{
				__instance.m_body.collisionDetectionMode = (CollisionDetectionMode)2;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "UpdateAttach")]
	[HarmonyPostfix]
	[HarmonyPriority(0)]
	private static void SmoothAttachedPlayerPostfix(Player __instance)
	{
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: 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_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: 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)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)__instance == (Object)null)
		{
			return;
		}
		int instanceID = ((Object)__instance).GetInstanceID();
		if (!NetworkConfig.IsModuleEnabled(NetworkConfig.ShipSyncEnabled) || !__instance.m_attached || !__instance.m_attachedToShip || (Object)(object)__instance.m_attachPoint == (Object)null)
		{
			PlayerVelocities.Remove(instanceID);
			return;
		}
		Transform attachPoint = __instance.m_attachPoint;
		Transform transform = ((Component)__instance).transform;
		Vector3 position = attachPoint.position;
		Quaternion rotation = attachPoint.rotation;
		if (!PlayerVelocities.TryGetValue(instanceID, out var value))
		{
			PlayerVelocities[instanceID] = Vector3.zero;
			transform.SetPositionAndRotation(position, rotation);
			NetworkDiagnostics.RecordShipAttachSnap();
			return;
		}
		Vector3 val = transform.position - position;
		if (((Vector3)(ref val)).sqrMagnitude >= 25f)
		{
			PlayerVelocities[instanceID] = Vector3.zero;
			transform.SetPositionAndRotation(position, rotation);
			NetworkDiagnostics.RecordShipAttachSnap();
		}
		else
		{
			transform.position = Vector3.SmoothDamp(transform.position, position, ref value, 0.08f);
			PlayerVelocities[instanceID] = value;
			float num = Mathf.Clamp01(Time.deltaTime / 0.1f);
			transform.rotation = Quaternion.Slerp(transform.rotation, rotation, num);
		}
	}

	[HarmonyPatch(typeof(Player), "AttachStop")]
	[HarmonyPrefix]
	private static void ClearCacheOnDetachPrefix(Player __instance)
	{
		if ((Object)(object)__instance != (Object)null)
		{
			PlayerVelocities.Remove(((Object)__instance).GetInstanceID());
		}
	}

	[HarmonyPatch(typeof(Player), "OnDestroy")]
	[HarmonyPrefix]
	private static void ClearCacheOnDestroyPrefix(Player __instance)
	{
		if ((Object)(object)__instance != (Object)null)
		{
			PlayerVelocities.Remove(((Object)__instance).GetInstanceID());
		}
	}
}
[HarmonyPatch]
internal static class SteamTransportPatches
{
	private const int VanillaSendRateMaxBytes = 153600;

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

	private static bool _applyInProgress;

	private static bool _steamInitializationObserved;

	private static bool _steamReady;

	private static bool _lastAttemptSteamNotInitialized;

	private static float _nextRetryTime;

	private static string _lastFingerprint = string.Empty;

	private static string _selectedUtilityTypeName = string.Empty;

	[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> RegisterGlobalCallbacksTranspiler(IEnumerable<CodeInstruction> instructions)
	{
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		List<int> list2 = new List<int>();
		for (int i = 0; i < list.Count; i++)
		{
			if (list[i].opcode == OpCodes.Ldc_I4 && list[i].operand is int num && num == 153600)
			{
				list2.Add(i);
			}
		}
		if (list2.Count != 1)
		{
			WarheimNetwork.Log.LogError((object)($"[Steam] Patch SendRateMax