Decompiled source of BetterNetworking Valheim v1.3.1

plugins\BetterNetworking_Valheim\DIT.BetterNetworking10.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Concurrent;
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.ExceptionServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ConditionalConfigSyncAPI;
using Costura;
using HarmonyLib;
using Ionic.Zlib;
using Microsoft.CodeAnalysis;
using PlayFab.Party;
using Steamworks;
using UnityEngine;
using ZstdSharp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyMetadata("PluginVersion", "1.3.1")]
[assembly: AssemblyCompany("DIT.BetterNetworking10")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.3.1.0")]
[assembly: AssemblyInformationalVersion("1.3.1+6e45f3aad5d989a744625d4c33394b14beddc495")]
[assembly: AssemblyProduct("DIT.BetterNetworking10")]
[assembly: AssemblyTitle("DIT.BetterNetworking10")]
[assembly: AssemblyVersion("1.3.1.0")]
[module: RefSafetyRules(11)]
internal class <Module>
{
	static <Module>()
	{
		AssemblyLoader.Attach();
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ConditionalConfigSyncAPI
{
	public enum SyncMode
	{
		AlwaysServerControlled,
		Conditional,
		AlwaysClientControlled
	}
	public enum RequirementMode
	{
		Fixed,
		Conditional
	}
	public sealed class ConfigSync
	{
		public const string PluginGuid = "_shudnal.ConditionalConfigSync";

		public const string MinimumCcsVersion = "1.0.10";

		private static readonly Version MinimumSupportedCcsVersion = new Version("1.0.10");

		private const string BridgeTypeName = "ConditionalConfigSync.SoftDependencyBridge";

		private const int MinimumBridgeApiVersion = 1;

		private readonly string _name;

		private readonly string _displayName;

		private readonly string _currentVersion;

		private readonly string _minimumRequiredVersion;

		private readonly bool _modRequired;

		private readonly RequirementMode _requirementMode;

		private readonly bool _allowClientConfigUpdatesWhenUnlocked;

		private readonly ManualLogSource _logger;

		private Type _bridgeType;

		private PropertyInfo _apiVersionProperty;

		private PropertyInfo _runtimeReadyProperty;

		private MethodInfo _createConfigSyncMethod;

		private MethodInfo _registerConfigEntryMethod;

		private MethodInfo _registerLockingConfigEntryMethod;

		private object _configSyncInstance;

		private bool _permanentFailure;

		private bool _permanentFailureLogged;

		private string _failureReason;

		public bool IsAvailable => EnsureBridge() && IsRuntimeReady();

		public bool IsActive => _configSyncInstance != null;

		public string FailureReason => _failureReason;

		public ConfigSync(string name, string displayName, string currentVersion, string minimumRequiredVersion = null, bool modRequired = false, RequirementMode requirementMode = RequirementMode.Fixed, bool allowClientConfigUpdatesWhenUnlocked = false, ManualLogSource logger = null)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				throw new ArgumentException("A stable mod GUID is required.", "name");
			}
			_name = name;
			_displayName = (string.IsNullOrWhiteSpace(displayName) ? name : displayName);
			_currentVersion = currentVersion;
			_minimumRequiredVersion = minimumRequiredVersion;
			_modRequired = modRequired;
			_requirementMode = requirementMode;
			_allowClientConfigUpdatesWhenUnlocked = allowClientConfigUpdatesWhenUnlocked;
			_logger = logger;
		}

		public bool TryActivate()
		{
			return EnsureContext();
		}

		public ConfigEntry<T> Bind<T>(ConfigFile configFile, string section, string key, T defaultValue, string description, SyncMode syncMode = SyncMode.Conditional, bool serverControlledByDefault = true)
		{
			if (configFile == null)
			{
				throw new ArgumentNullException("configFile");
			}
			ConfigEntry<T> val = configFile.Bind<T>(section, key, defaultValue, description);
			RegisterConfigEntry<T>(val, syncMode, serverControlledByDefault);
			return val;
		}

		public ConfigEntry<T> Bind<T>(ConfigFile configFile, string section, string key, T defaultValue, ConfigDescription description, SyncMode syncMode = SyncMode.Conditional, bool serverControlledByDefault = true)
		{
			if (configFile == null)
			{
				throw new ArgumentNullException("configFile");
			}
			ConfigEntry<T> val = configFile.Bind<T>(section, key, defaultValue, description);
			RegisterConfigEntry<T>(val, syncMode, serverControlledByDefault);
			return val;
		}

		public bool RegisterConfigEntry<T>(ConfigEntry<T> entry, SyncMode syncMode = SyncMode.Conditional, bool serverControlledByDefault = true)
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (!EnsureContext())
			{
				return false;
			}
			try
			{
				Invoke(_registerConfigEntryMethod, null, new object[4]
				{
					_configSyncInstance,
					entry,
					syncMode.ToString(),
					serverControlledByDefault
				});
				return true;
			}
			catch (Exception exception)
			{
				LogWarning("Failed to register config entry '" + ((ConfigEntryBase)entry).Definition.Section + " -> " + ((ConfigEntryBase)entry).Definition.Key + "' with Conditional Config Sync. The entry remains a normal local BepInEx setting. " + GetInnermostMessage(exception));
				return false;
			}
		}

		public bool RegisterLockingConfigEntry<T>(ConfigEntry<T> entry) where T : IConvertible
		{
			if (entry == null)
			{
				throw new ArgumentNullException("entry");
			}
			if (!EnsureContext())
			{
				return false;
			}
			try
			{
				Invoke(_registerLockingConfigEntryMethod, null, new object[2] { _configSyncInstance, entry });
				return true;
			}
			catch (Exception exception)
			{
				LogWarning("Failed to register locking config entry '" + ((ConfigEntryBase)entry).Definition.Section + " -> " + ((ConfigEntryBase)entry).Definition.Key + "' with Conditional Config Sync. The entry remains a normal local BepInEx setting. " + GetInnermostMessage(exception));
				return false;
			}
		}

		public ConfigEntry<T> BindLocking<T>(ConfigFile configFile, string section, string key, T defaultValue, string description) where T : IConvertible
		{
			if (configFile == null)
			{
				throw new ArgumentNullException("configFile");
			}
			ConfigEntry<T> val = configFile.Bind<T>(section, key, defaultValue, description);
			RegisterLockingConfigEntry<T>(val);
			return val;
		}

		public ConfigEntry<T> BindLocking<T>(ConfigFile configFile, string section, string key, T defaultValue, ConfigDescription description) where T : IConvertible
		{
			if (configFile == null)
			{
				throw new ArgumentNullException("configFile");
			}
			ConfigEntry<T> val = configFile.Bind<T>(section, key, defaultValue, description);
			RegisterLockingConfigEntry<T>(val);
			return val;
		}

		private bool EnsureContext()
		{
			if (_configSyncInstance != null)
			{
				return true;
			}
			if (_permanentFailure || !EnsureBridge() || !IsRuntimeReady())
			{
				return false;
			}
			try
			{
				_configSyncInstance = Invoke(_createConfigSyncMethod, null, new object[7]
				{
					_name,
					_displayName,
					_currentVersion,
					_minimumRequiredVersion,
					_modRequired,
					_requirementMode.ToString(),
					_allowClientConfigUpdatesWhenUnlocked
				});
				if (_configSyncInstance == null)
				{
					SetPermanentFailure("Conditional Config Sync returned no consumer context.", null);
					return false;
				}
				return true;
			}
			catch (Exception exception)
			{
				SetPermanentFailure("Failed to create the Conditional Config Sync consumer context.", exception);
				return false;
			}
		}

		private bool EnsureBridge()
		{
			if (_bridgeType != null)
			{
				return true;
			}
			if (_permanentFailure)
			{
				return false;
			}
			try
			{
				if (!Chainloader.PluginInfos.TryGetValue("_shudnal.ConditionalConfigSync", out var value) || value == null || (Object)(object)value.Instance == (Object)null)
				{
					return false;
				}
				if (value.Metadata == null || value.Metadata.Version == null || value.Metadata.Version.CompareTo(MinimumSupportedCcsVersion) < 0)
				{
					return false;
				}
				Assembly assembly = ((object)value.Instance).GetType().Assembly;
				Type type = assembly.GetType("ConditionalConfigSync.SoftDependencyBridge", throwOnError: false);
				if (type == null)
				{
					SetPermanentFailure("The installed Conditional Config Sync version does not expose the optional soft-dependency bridge.", null);
					return false;
				}
				PropertyInfo property = type.GetProperty("ApiVersion", BindingFlags.Static | BindingFlags.Public);
				PropertyInfo property2 = type.GetProperty("IsRuntimeReady", BindingFlags.Static | BindingFlags.Public);
				MethodInfo method = type.GetMethod("CreateConfigSync", BindingFlags.Static | BindingFlags.Public, null, new Type[7]
				{
					typeof(string),
					typeof(string),
					typeof(string),
					typeof(string),
					typeof(bool),
					typeof(string),
					typeof(bool)
				}, null);
				MethodInfo method2 = type.GetMethod("RegisterConfigEntry", BindingFlags.Static | BindingFlags.Public, null, new Type[4]
				{
					typeof(object),
					typeof(ConfigEntryBase),
					typeof(string),
					typeof(bool)
				}, null);
				MethodInfo method3 = type.GetMethod("RegisterLockingConfigEntry", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(object),
					typeof(ConfigEntryBase)
				}, null);
				if (property == null || property2 == null || method == null || method2 == null || method3 == null)
				{
					SetPermanentFailure("The installed Conditional Config Sync soft-dependency bridge is incomplete or incompatible.", null);
					return false;
				}
				if (!(property.GetValue(null, null) is int num) || num < 1)
				{
					SetPermanentFailure("The installed Conditional Config Sync soft-dependency bridge version is not supported.", null);
					return false;
				}
				_bridgeType = type;
				_apiVersionProperty = property;
				_runtimeReadyProperty = property2;
				_createConfigSyncMethod = method;
				_registerConfigEntryMethod = method2;
				_registerLockingConfigEntryMethod = method3;
				return true;
			}
			catch (Exception exception)
			{
				SetPermanentFailure("Failed to resolve the Conditional Config Sync soft-dependency bridge.", exception);
				return false;
			}
		}

		private bool IsRuntimeReady()
		{
			try
			{
				object value = _runtimeReadyProperty.GetValue(null, null);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch (Exception exception)
			{
				SetPermanentFailure("Failed to query the Conditional Config Sync runtime state.", exception);
				return false;
			}
		}

		private static object Invoke(MethodInfo method, object target, object[] arguments)
		{
			try
			{
				return method.Invoke(target, arguments);
			}
			catch (TargetInvocationException ex) when (ex.InnerException != null)
			{
				ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
				throw;
			}
		}

		private void SetPermanentFailure(string message, Exception exception)
		{
			_permanentFailure = true;
			_failureReason = ((exception == null) ? message : (message + " " + GetInnermostMessage(exception)));
			if (!_permanentFailureLogged)
			{
				_permanentFailureLogged = true;
				LogWarning(_failureReason);
			}
		}

		private void LogWarning(string message)
		{
			if (_logger != null)
			{
				_logger.LogWarning((object)message);
			}
		}

		private static string GetInnermostMessage(Exception exception)
		{
			Exception ex = exception;
			while (ex.InnerException != null)
			{
				ex = ex.InnerException;
			}
			return ex.Message;
		}
	}
}
namespace DIT.BetterNetworking10
{
	[BepInPlugin("DIT.BetterNetworking10", "Better Networking 1.0 Safe", "1.3.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("CW_Jesse.BetterNetworking")]
	[BepInIncompatibility("org.bepinex.plugins.network")]
	[BepInIncompatibility("be.sebastienvercammen.valheim.netcompression")]
	[BepInIncompatibility("com.github.dalayeth.Networkfix")]
	[BepInIncompatibility("Steel.ValheimMod")]
	public sealed class BetterNetworking10 : BaseUnityPlugin
	{
		public const string PluginGuid = "DIT.BetterNetworking10";

		public const string PluginName = "Better Networking 1.0 Safe";

		public const string PluginVersion = "1.3.1";

		private const string MinimumSyncCompatibleVersion = "1.3.0";

		internal static ConfigEntry<bool> LockConfiguration;

		internal static ConfigEntry<CompatibilityMode> Mode;

		internal static ConfigEntry<bool> EnableCompression;

		internal static ConfigEntry<bool> EnableQueueSize;

		internal static ConfigEntry<bool> EnableUpdateRate;

		internal static ConfigEntry<bool> EnableSendRate;

		internal static ConfigEntry<bool> EnableConnectionBuffer;

		internal static ConfigEntry<bool> EnablePortalGhostFix;

		internal static ConfigEntry<QueueSizeOption> QueueSize;

		internal static ConfigEntry<UpdateRateOption> UpdateRate;

		internal static ConfigEntry<SendRateOption> MinimumSendRate;

		internal static ConfigEntry<SendRateOption> MaximumSendRate;

		internal static ConfigEntry<ForceCrossplayOption> ForceCrossplay;

		internal static ConfigEntry<int> PlayerLimit;

		private Harmony _harmony;

		private ConfigSync _configSync;

		internal static BetterNetworking10 Instance { get; private set; }

		internal static BNLog Log { get; private set; }

		internal static bool CompressionRuntimeAvailable { get; set; }

		internal static bool SafeMode => Mode.Value == CompatibilityMode.VanillaSafe;

		internal static bool CompressionActive => CompressionRuntimeAvailable && EnableCompression.Value;

		internal static bool QueueActive => !SafeMode && EnableQueueSize.Value && QueueSize.Value != QueueSizeOption.Vanilla;

		internal static bool UpdateRateActive => !SafeMode && EnableUpdateRate.Value && UpdateRate.Value != UpdateRateOption.Percent100;

		internal static bool SendRateActive => !SafeMode && EnableSendRate.Value;

		internal static bool ConnectionBufferActive => !SafeMode && EnableConnectionBuffer.Value;

		internal static bool PortalGhostFixActive => EnablePortalGhostFix.Value;

		internal static int QueueBudgetBytes => QueueSize.Value switch
		{
			QueueSizeOption.KB80 => 81920, 
			QueueSizeOption.KB64 => 65536, 
			QueueSizeOption.KB48 => 49152, 
			QueueSizeOption.KB32 => 32768, 
			_ => 10240, 
		};

		internal static float UpdateRateMultiplier => UpdateRate.Value switch
		{
			UpdateRateOption.Percent75 => 0.75f, 
			UpdateRateOption.Percent50 => 0.5f, 
			_ => 1f, 
		};

		internal static bool IsDedicated => ((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.IsDedicated() : Application.isBatchMode;

		private void Awake()
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Expected O, but got Unknown
			Instance = this;
			bool flag = !File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath);
			_configSync = new ConfigSync("DIT.BetterNetworking10", "Better Networking 1.0 Safe", "1.3.1", "1.3.0", modRequired: false, RequirementMode.Conditional, allowClientConfigUpdatesWhenUnlocked: true, ((BaseUnityPlugin)this).Logger);
			BindConfig();
			Log = new BNLog(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config);
			if (flag)
			{
				LegacyConfigMigration.TryImport(((BaseUnityPlugin)this).Config, Log);
			}
			GameVersion val = default(GameVersion);
			((GameVersion)(ref val))..ctor(1, 0, 21);
			if (Version.CurrentVersion < val || Version.CurrentVersion > val)
			{
				Log.Warning($"This build targets Valheim {val}, compiled against network version {41u}; running {Version.CurrentVersion}. Check PATCH FAILED messages and retest networking after game updates.");
			}
			else
			{
				Log.Message($"Valheim target version matched: {Version.CurrentVersion}, compiled against network version {41u}");
			}
			_harmony = new Harmony("DIT.BetterNetworking10");
			PatchInstaller.Install(_harmony);
			PortalGhostRefresh.Initialize();
			RegisterRuntimeConfigHandlers();
			if (_configSync.IsActive)
			{
				Log.Message("Conditional Config Sync active (optional integration, CCS >= 1.0.10)");
			}
			else if (Chainloader.PluginInfos.ContainsKey("_shudnal.ConditionalConfigSync"))
			{
				Log.Warning("Conditional Config Sync is installed but optional synchronization is inactive; CCS 1.0.10 or newer is required");
			}
		}

		private void Update()
		{
			PortalGhostRefresh.Tick();
		}

		private void OnDestroy()
		{
			UnregisterRuntimeConfigHandlers();
			CompressionState.Clear();
			ConnectionBufferPatch.Clear();
			PortalGhostRefresh.Clear();
			SteamSendRatePatch.Clear();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void BindConfig()
		{
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Expected O, but got Unknown
			LockConfiguration = _configSync.BindLocking(((BaseUnityPlugin)this).Config, "00 - Compatibility", "Lock Configuration", defaultValue: true, "When Conditional Config Sync is installed, only server administrators may change server-controlled settings. Has no effect without CCS.");
			Mode = _configSync.Bind(((BaseUnityPlugin)this).Config, "00 - Compatibility", "Mode", CompatibilityMode.Balanced, "Balanced: the enabled feature switches below are applied. VanillaSafe: only compression is allowed; ZDO scheduling, queue, and Steam send-rate changes are disabled.");
			EnableCompression = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "Compression", defaultValue: true, "Compress traffic only when both peers run this compatible build.");
			EnableQueueSize = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "Queue Size", defaultValue: true, "Changes the ZDO send budget directly. Disabled in VanillaSafe mode.");
			EnableUpdateRate = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "Update Rate", defaultValue: true, "Allows a lower ZDO update frequency. Disabled in VanillaSafe mode.");
			EnableSendRate = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "Steam Send Rate", defaultValue: true, "Changes Steamworks send-rate limits. Has no effect with PlayFab/crossplay. Disabled in VanillaSafe mode.");
			EnableConnectionBuffer = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "New Connection ZDO Buffer", defaultValue: false, "Experimental and client-local because it runs before server config synchronization. Buffers early ZDOData packets per connection until ZDOMan registers the peer. OFF by default.", SyncMode.AlwaysClientControlled);
			EnablePortalGhostFix = _configSync.Bind(((BaseUnityPlugin)this).Config, "01 - Features", "Portal Ghost Fix", defaultValue: false, "Optional server-side portal observer refresh. Re-sends a teleported player's ZDO and related networked status-effect ZDOs; vanilla clients are supported. Disable the standalone Sfantul mod when using this.", SyncMode.AlwaysServerControlled);
			QueueSize = _configSync.Bind(((BaseUnityPlugin)this).Config, "02 - Networking", "Queue Size", QueueSizeOption.KB32, "ZDO send budget based on Valheim's reported queue size. Vanilla leaves the game's 10 KB budget unchanged.");
			UpdateRate = _configSync.Bind(((BaseUnityPlugin)this).Config, "02 - Networking", "Update Rate", UpdateRateOption.Percent100, "100 = vanilla 20 Hz timer; 75 = 15 Hz; 50 = 10 Hz.");
			MinimumSendRate = _configSync.Bind(((BaseUnityPlugin)this).Config, "03 - Steamworks", "Minimum Send Rate", SendRateOption.KB256, "Steamworks minimum send rate in KB/s. Keep this below the real upload capacity.");
			MaximumSendRate = _configSync.Bind(((BaseUnityPlugin)this).Config, "03 - Steamworks", "Maximum Send Rate", SendRateOption.KB1024, "Steamworks maximum send rate in KB/s.");
			ForceCrossplay = _configSync.Bind(((BaseUnityPlugin)this).Config, "04 - Dedicated Server", "Force Crossplay", ForceCrossplayOption.Vanilla, "Vanilla follows the server command line; PlayFab or Steamworks overrides the backend before server initialization. Requires restart and is disabled in VanillaSafe mode.", SyncMode.AlwaysServerControlled);
			PlayerLimit = _configSync.Bind(((BaseUnityPlugin)this).Config, "04 - Dedicated Server", "Player Limit", 10, new ConfigDescription("Dedicated server player limit. Requires restart.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 127), Array.Empty<object>()), SyncMode.AlwaysServerControlled);
		}

		private void RegisterRuntimeConfigHandlers()
		{
			EnableCompression.SettingChanged += OnCompressionSettingChanged;
			Mode.SettingChanged += OnNetworkRuntimeSettingChanged;
			EnableSendRate.SettingChanged += OnNetworkRuntimeSettingChanged;
			MinimumSendRate.SettingChanged += OnNetworkRuntimeSettingChanged;
			MaximumSendRate.SettingChanged += OnNetworkRuntimeSettingChanged;
			EnableConnectionBuffer.SettingChanged += OnNetworkRuntimeSettingChanged;
		}

		private void UnregisterRuntimeConfigHandlers()
		{
			if (EnableCompression != null)
			{
				EnableCompression.SettingChanged -= OnCompressionSettingChanged;
			}
			if (Mode != null)
			{
				Mode.SettingChanged -= OnNetworkRuntimeSettingChanged;
			}
			if (EnableSendRate != null)
			{
				EnableSendRate.SettingChanged -= OnNetworkRuntimeSettingChanged;
			}
			if (MinimumSendRate != null)
			{
				MinimumSendRate.SettingChanged -= OnNetworkRuntimeSettingChanged;
			}
			if (MaximumSendRate != null)
			{
				MaximumSendRate.SettingChanged -= OnNetworkRuntimeSettingChanged;
			}
			if (EnableConnectionBuffer != null)
			{
				EnableConnectionBuffer.SettingChanged -= OnNetworkRuntimeSettingChanged;
			}
		}

		private static void OnCompressionSettingChanged(object sender, EventArgs eventArgs)
		{
			CompressionPatch.RefreshNegotiation();
		}

		private static void OnNetworkRuntimeSettingChanged(object sender, EventArgs eventArgs)
		{
			SteamSendRatePatch.Refresh();
			if (!ConnectionBufferActive)
			{
				ConnectionBufferPatch.Clear();
			}
		}

		internal static int RateBytesPerSecond(SendRateOption option)
		{
			return (int)option * 1024;
		}
	}
	public enum CompatibilityMode
	{
		Balanced,
		VanillaSafe
	}
	public enum QueueSizeOption
	{
		Vanilla = 10,
		KB32 = 32,
		KB48 = 48,
		KB64 = 64,
		KB80 = 80
	}
	public enum UpdateRateOption
	{
		Percent100 = 100,
		Percent75 = 75,
		Percent50 = 50
	}
	public enum SendRateOption
	{
		KB150 = 150,
		KB256 = 256,
		KB512 = 512,
		KB768 = 768,
		KB1024 = 1024
	}
	public enum ForceCrossplayOption
	{
		Vanilla,
		PlayFab,
		Steamworks
	}
	internal static class PatchInstaller
	{
		private static int _applied;

		private static int _skipped;

		private static int _failed;

		internal static void Install(Harmony harmony)
		{
			BetterNetworking10.CompressionRuntimeAvailable = false;
			try
			{
				CompressionCodec.Initialize();
				CompressionFrame.SelfTest();
				BetterNetworking10.CompressionRuntimeAvailable = true;
				InstallCompression(harmony);
			}
			catch (Exception ex)
			{
				Skip("Compression", "self-test failed: " + ex.GetType().Name + ": " + ex.Message);
				BetterNetworking10.Log.Error("Compression was left disabled because its startup self-test failed");
			}
			Patch(harmony, "ZDO queue budget (runtime guarded)", AccessTools.DeclaredMethod(typeof(ZDOMan), "SendZDOs", (Type[])null, (Type[])null), null, null, "Transpiler", typeof(QueueBudgetPatch));
			Patch(harmony, "ZDO update rate (runtime guarded)", AccessTools.DeclaredMethod(typeof(ZDOMan), "SendZDOToPeers2", (Type[])null, (Type[])null), "Prefix", null, null, typeof(UpdateRatePatch));
			Patch(harmony, "Steam send rate (runtime guarded)", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "RegisterGlobalCallbacks", (Type[])null, (Type[])null), null, "Postfix", null, typeof(SteamSendRatePatch));
			InstallConnectionBuffer(harmony);
			InstallDedicatedTweaks(harmony);
			BetterNetworking10.Log.Message($"Patch audit complete: {_applied} applied, {_skipped} skipped, {_failed} failed. Mode={BetterNetworking10.Mode.Value}");
		}

		private static void InstallCompression(Harmony harmony)
		{
			Patch(harmony, "Compression connect", AccessTools.DeclaredMethod(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null), null, "OnConnectPostfix", null, typeof(CompressionPatch));
			Patch(harmony, "Compression disconnect pre", AccessTools.DeclaredMethod(typeof(ZNet), "Disconnect", (Type[])null, (Type[])null), "OnDisconnectPrefix", null, null, typeof(CompressionPatch));
			Patch(harmony, "Compression disconnect post", AccessTools.DeclaredMethod(typeof(ZNet), "Disconnect", (Type[])null, (Type[])null), null, "OnDisconnectPostfix", null, typeof(CompressionPatch));
			Patch(harmony, "Steam compress-once send", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "Send", new Type[1] { typeof(ZPackage) }, (Type[])null), "SendPrefix", null, null, typeof(SteamCompressionPatch));
			Patch(harmony, "Steam receive", AccessTools.DeclaredMethod(typeof(ZSteamSocket), "Recv", (Type[])null, (Type[])null), null, "ReceivePostfix", null, typeof(SteamCompressionPatch));
			Patch(harmony, "PlayFab socket ctor()", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), Type.EmptyTypes, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab socket ctor(string,callback)", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), new Type[2]
			{
				typeof(string),
				typeof(Action<PlayFabMatchmakingServerData>)
			}, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab socket ctor(player)", AccessTools.DeclaredConstructor(typeof(ZPlayFabSocket), new Type[1] { typeof(PlayFabPlayer) }, false), null, "SocketOpenPostfix", null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab socket dispose", AccessTools.DeclaredMethod(typeof(ZPlayFabSocket), "Dispose", (Type[])null, (Type[])null), null, "SocketClosePostfix", null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab compress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "DoCompress", (Type[])null, (Type[])null), "CompressPrefix", null, null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab decompress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "DoUncompress", (Type[])null, (Type[])null), "DecompressPrefix", null, null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab direct compress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "CompressOnThisThread", (Type[])null, (Type[])null), "DirectCompressPrefix", null, null, typeof(PlayFabCompressionPatch));
			Patch(harmony, "PlayFab direct decompress", AccessTools.DeclaredMethod(typeof(PlayFabZLibWorkQueue), "UncompressOnThisThread", (Type[])null, (Type[])null), "DirectDecompressPrefix", null, null, typeof(PlayFabCompressionPatch));
		}

		private static void InstallConnectionBuffer(Harmony harmony)
		{
			Patch(harmony, "Connection buffer start", AccessTools.DeclaredMethod(typeof(ZNet), "OnNewConnection", (Type[])null, (Type[])null), null, "OnNewConnectionPostfix", null, typeof(ConnectionBufferPatch));
			Patch(harmony, "Connection buffer replay", AccessTools.DeclaredMethod(typeof(ZDOMan), "AddPeer", (Type[])null, (Type[])null), null, "AddPeerPostfix", null, typeof(ConnectionBufferPatch));
			Patch(harmony, "Connection buffer clear", AccessTools.DeclaredMethod(typeof(ZNet), "Shutdown", (Type[])null, (Type[])null), null, "ShutdownPostfix", null, typeof(ConnectionBufferPatch));
		}

		private static void InstallDedicatedTweaks(Harmony harmony)
		{
			Patch(harmony, "Dedicated backend selection (runtime guarded)", AccessTools.DeclaredMethod(typeof(ZNet), "SetServer", new Type[6]
			{
				typeof(bool),
				typeof(bool),
				typeof(bool),
				typeof(string),
				typeof(string),
				typeof(World)
			}, (Type[])null), "SetServerPrefix", null, null, typeof(DedicatedPatch));
			Patch(harmony, "Dedicated player limit (runtime value)", AccessTools.DeclaredMethod(typeof(ZNet), "RPC_PeerInfo", (Type[])null, (Type[])null), null, null, "PlayerLimitTranspiler", typeof(DedicatedPatch));
		}

		private static void Patch(Harmony harmony, string label, MethodBase target, string prefix = null, string postfix = null, string transpiler = null, Type patchType = null)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (target == null)
			{
				_failed++;
				BetterNetworking10.Log.Error("PATCH FAILED " + label + ": target method not found; feature left vanilla");
				return;
			}
			try
			{
				HarmonyMethod val = ((prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, prefix, (Type[])null, (Type[])null)));
				HarmonyMethod val2 = ((postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, postfix, (Type[])null, (Type[])null)));
				HarmonyMethod val3 = ((transpiler == null) ? ((HarmonyMethod)null) : new HarmonyMethod(AccessTools.DeclaredMethod(patchType, transpiler, (Type[])null, (Type[])null)));
				if ((prefix != null && val.method == null) || (postfix != null && val2.method == null) || (transpiler != null && val3.method == null))
				{
					throw new MissingMethodException("patch method not found");
				}
				harmony.Patch(target, val, val2, val3, (HarmonyMethod)null, (HarmonyMethod)null);
				_applied++;
				BetterNetworking10.Log.Message("PATCH OK " + label + ": " + target.DeclaringType?.Name + "." + target.Name);
			}
			catch (Exception ex)
			{
				_failed++;
				BetterNetworking10.Log.Error("PATCH FAILED " + label + ": " + ex.GetType().Name + ": " + ex.Message + "; feature left vanilla");
			}
		}

		private static void Skip(string label, string reason)
		{
			_skipped++;
			BetterNetworking10.Log.Message("PATCH SKIP " + label + ": " + reason);
		}
	}
	internal sealed class BNLog
	{
		internal enum LogLevel
		{
			Warning,
			Message,
			Info
		}

		private readonly ManualLogSource _logger;

		private readonly ConfigEntry<LogLevel> _level;

		internal bool IsInfo => _level.Value >= LogLevel.Info;

		internal BNLog(ManualLogSource logger, ConfigFile config)
		{
			_logger = logger;
			_level = config.Bind<LogLevel>("99 - Logging", "Log Level", LogLevel.Message, "Warning, Message, or Info (verbose packet compression logging). ");
		}

		internal void Error(object value)
		{
			_logger.LogError(value);
		}

		internal void Warning(object value)
		{
			_logger.LogWarning(value);
		}

		internal void Message(object value)
		{
			if (_level.Value >= LogLevel.Message)
			{
				_logger.LogMessage(value);
			}
		}

		internal void Info(object value)
		{
			if (_level.Value >= LogLevel.Info)
			{
				_logger.LogInfo(value);
			}
		}
	}
	internal static class CompressionCodec
	{
		private const string DictionaryResource = "DIT.BetterNetworking10.dict.small";

		private static Compressor _compressor;

		private static Decompressor _decompressor;

		private static readonly object Sync = new object();

		internal static void Initialize()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("DIT.BetterNetworking10.dict.small");
			if (stream == null)
			{
				throw new InvalidOperationException("Embedded Zstd dictionary is missing");
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num == 0)
				{
					throw new EndOfStreamException("Could not read embedded Zstd dictionary");
				}
			}
			_compressor = new Compressor(1);
			_compressor.LoadDictionary(array);
			_decompressor = new Decompressor();
			_decompressor.LoadDictionary(array);
		}

		internal static byte[] Compress(byte[] input)
		{
			lock (Sync)
			{
				return _compressor.Wrap((ReadOnlySpan<byte>)input).ToArray();
			}
		}

		internal static byte[] Decompress(byte[] input)
		{
			lock (Sync)
			{
				return _decompressor.Unwrap((ReadOnlySpan<byte>)input, int.MaxValue).ToArray();
			}
		}
	}
	internal static class CompressionFrame
	{
		private static readonly byte[] Magic = new byte[4] { 66, 78, 49, 48 };

		private const int HeaderSize = 5;

		private const byte RawPayload = 0;

		private const byte CompressedPayload = 1;

		internal static byte[] Encode(byte[] input)
		{
			return Encode(input, diagnostics: true);
		}

		private static byte[] Encode(byte[] input, bool diagnostics)
		{
			if (HasHeader(input))
			{
				if (diagnostics)
				{
					BetterNetworking10.Log.Warning("Prevented duplicate Better Networking compression frame");
				}
				return input;
			}
			byte[] array = CompressionCodec.Compress(input);
			bool flag = array.Length < input.Length;
			byte[] array2 = (flag ? array : input);
			byte[] array3 = new byte[5 + array2.Length];
			Buffer.BlockCopy(Magic, 0, array3, 0, Magic.Length);
			array3[Magic.Length] = (byte)(flag ? 1 : 0);
			Buffer.BlockCopy(array2, 0, array3, 5, array2.Length);
			if (diagnostics && BetterNetworking10.Log.IsInfo && input.Length > 256)
			{
				if (flag)
				{
					BetterNetworking10.Log.Info($"Compression sent {input.Length} B -> {array3.Length} B ({100f * (float)array3.Length / (float)input.Length:0}%, framed)");
				}
				else
				{
					BetterNetworking10.Log.Info($"Compression bypassed for {input.Length} B packet because it would grow ({array3.Length} B framed)");
				}
			}
			return array3;
		}

		internal static bool TryDecode(byte[] input, out byte[] output)
		{
			output = null;
			if (!HasHeader(input))
			{
				return false;
			}
			int num = input.Length - 5;
			byte[] array = new byte[num];
			Buffer.BlockCopy(input, 5, array, 0, num);
			byte b = input[Magic.Length];
			switch (b)
			{
			case 0:
				output = array;
				break;
			case 1:
				output = CompressionCodec.Decompress(array);
				break;
			default:
				throw new InvalidDataException($"Unknown Better Networking frame type {b}");
			}
			if (BetterNetworking10.Log.IsInfo && output.Length > 256)
			{
				BetterNetworking10.Log.Info($"Compression received {input.Length} B -> {output.Length} B (framed)");
			}
			return true;
		}

		internal static bool HasHeader(byte[] input)
		{
			if (input == null || input.Length < 5)
			{
				return false;
			}
			for (int i = 0; i < Magic.Length; i++)
			{
				if (input[i] != Magic[i])
				{
					return false;
				}
			}
			return true;
		}

		internal static void SelfTest()
		{
			byte[] array = Enumerable.Repeat((byte)90, 1024).ToArray();
			byte[] array2 = Encode(array);
			if (!TryDecode(array2, out var output) || !((ReadOnlySpan<byte>)array).SequenceEqual((ReadOnlySpan<byte>)output))
			{
				throw new InvalidDataException("Compression frame round-trip failed");
			}
			if (array2 != Encode(array2, diagnostics: false))
			{
				throw new InvalidDataException("Compression frame duplicate guard failed");
			}
		}
	}
	internal static class CompressionState
	{
		internal sealed class PeerState
		{
			internal int Version;

			internal bool Enabled;

			internal volatile bool Sending;

			internal volatile bool Receiving;

			internal volatile bool WarnedUnframed;
		}

		internal const int ProtocolVersion = 8;

		private static readonly ConcurrentDictionary<ISocket, PeerState> Peers = new ConcurrentDictionary<ISocket, PeerState>();

		internal static void Add(ISocket socket)
		{
			if (socket != null)
			{
				Peers[socket] = new PeerState();
			}
		}

		internal static void Remove(ISocket socket)
		{
			if (socket != null)
			{
				Peers.TryRemove(socket, out var _);
			}
		}

		internal static bool TryGet(ISocket socket, out PeerState state)
		{
			state = null;
			return socket != null && Peers.TryGetValue(socket, out state);
		}

		internal static void Clear()
		{
			Peers.Clear();
		}
	}
	internal static class PeerLookup
	{
		internal static ZNetPeer ByRpc(ZRpc rpc)
		{
			if (rpc == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return null;
			}
			return ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer peer) => peer.m_rpc == rpc));
		}

		internal static string Name(ZNetPeer peer)
		{
			if (peer == null)
			{
				return "[unknown]";
			}
			if (peer.m_server)
			{
				return "[server]";
			}
			string text = ((peer.m_socket == null) ? "?" : peer.m_socket.GetHostName());
			return peer.m_playerName + "[" + text + "]";
		}
	}
	internal static class CompressionPatch
	{
		private const string RpcVersion = "CW_Jesse.BetterNetworking.CompressionVersion";

		private const string RpcEnabled = "CW_Jesse.BetterNetworking.CompressionEnabled";

		private const string RpcStarted = "CW_Jesse.BetterNetworking.CompressedStarted";

		internal static void OnConnectPostfix(ZNetPeer peer)
		{
			if (peer?.m_socket != null && peer.m_rpc != null)
			{
				CompressionState.Add(peer.m_socket);
				peer.m_rpc.Register<int>("CW_Jesse.BetterNetworking.CompressionVersion", (Action<ZRpc, int>)ReceiveVersion);
				peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressionEnabled", (Action<ZRpc, bool>)ReceiveEnabled);
				peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressedStarted", (Action<ZRpc, bool>)ReceiveStarted);
				peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionVersion", new object[1] { 8 });
				BetterNetworking10.Log.Message("Compression peer connected: " + PeerLookup.Name(peer));
			}
		}

		internal static void OnDisconnectPrefix(ZNetPeer peer)
		{
			BetterNetworking10.Log.Message("Compression peer disconnected: " + PeerLookup.Name(peer));
		}

		internal static void OnDisconnectPostfix(ZNetPeer peer)
		{
			CompressionState.Remove(peer?.m_socket);
		}

		internal static void RefreshNegotiation()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			ZNetPeer[] array = ZNet.instance.GetPeers().ToArray();
			foreach (ZNetPeer val in array)
			{
				if (val?.m_rpc != null && val.m_socket != null && CompressionState.TryGet(val.m_socket, out var _))
				{
					try
					{
						SendEnabled(val);
					}
					catch (Exception ex)
					{
						BetterNetworking10.Log.Warning("Could not refresh compression state for " + PeerLookup.Name(val) + ": " + ex.Message);
					}
				}
			}
		}

		private static void ReceiveVersion(ZRpc rpc, int version)
		{
			ZNetPeer val = PeerLookup.ByRpc(rpc);
			if (val != null && CompressionState.TryGet(val.m_socket, out var state))
			{
				state.Version = version;
				if (version == 8)
				{
					BetterNetworking10.Log.Message($"Compression compatible with {PeerLookup.Name(val)} (protocol {version})");
					SendEnabled(val);
				}
				else if (version > 0)
				{
					BetterNetworking10.Log.Warning($"Compression disabled with {PeerLookup.Name(val)}: protocol {version}, expected {8}");
				}
			}
		}

		private static void ReceiveEnabled(ZRpc rpc, bool enabled)
		{
			ZNetPeer val = PeerLookup.ByRpc(rpc);
			if (val != null && CompressionState.TryGet(val.m_socket, out var state))
			{
				state.Enabled = enabled;
				SendStarted(val, BetterNetworking10.CompressionActive && enabled && state.Version == 8);
			}
		}

		private static void ReceiveStarted(ZRpc rpc, bool started)
		{
			ZNetPeer val = PeerLookup.ByRpc(rpc);
			if (val != null && CompressionState.TryGet(val.m_socket, out var state))
			{
				state.Receiving = started;
				state.WarnedUnframed = false;
				BetterNetworking10.Log.Message($"Compression from {PeerLookup.Name(val)}: {started}");
			}
		}

		private static void SendEnabled(ZNetPeer peer)
		{
			peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionEnabled", new object[1] { BetterNetworking10.CompressionActive });
			if (CompressionState.TryGet(peer.m_socket, out var state))
			{
				SendStarted(peer, BetterNetworking10.CompressionActive && state.Enabled && state.Version == 8);
			}
		}

		private static void SendStarted(ZNetPeer peer, bool started)
		{
			if (CompressionState.TryGet(peer.m_socket, out var state) && state.Sending != started)
			{
				peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressedStarted", new object[1] { started });
				Flush(peer.m_socket);
				state.Sending = started;
				BetterNetworking10.Log.Message($"Compression to {PeerLookup.Name(peer)}: {started}");
			}
		}

		private static void Flush(ISocket socket)
		{
			if (socket is ZSteamSocket)
			{
				socket.Flush();
				return;
			}
			ZPlayFabSocket val = (ZPlayFabSocket)(object)((socket is ZPlayFabSocket) ? socket : null);
			if (val != null)
			{
				PlayFabCompressionPatch.Flush(val);
			}
		}
	}
	internal static class SteamCompressionPatch
	{
		internal static void SendPrefix(ZSteamSocket __instance, ref ZPackage pkg)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			if (pkg != null && CompressionState.TryGet((ISocket)(object)__instance, out var state) && state.Sending)
			{
				pkg = new ZPackage(CompressionFrame.Encode(pkg.GetArray()));
			}
		}

		internal static void ReceivePostfix(ZSteamSocket __instance, ref ZPackage __result)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			if (__result == null || !CompressionState.TryGet((ISocket)(object)__instance, out var state))
			{
				return;
			}
			byte[] array = __result.GetArray();
			try
			{
				if (CompressionFrame.TryDecode(array, out var output))
				{
					__result = new ZPackage(output);
					state.WarnedUnframed = false;
					if (!state.Receiving)
					{
						state.Receiving = true;
						BetterNetworking10.Log.Warning("Received framed Steamworks packet before compression-start state; recovered automatically");
					}
				}
				else if (state.Receiving && !state.WarnedUnframed)
				{
					state.WarnedUnframed = true;
					BetterNetworking10.Log.Warning("Received an unframed Steamworks packet while compression was active; packet preserved");
				}
			}
			catch (Exception ex)
			{
				BetterNetworking10.Log.Error("Invalid Better Networking Steamworks frame dropped: " + ex.Message);
				__result = null;
			}
		}
	}
	internal static class PlayFabCompressionPatch
	{
		private static readonly ConcurrentDictionary<PlayFabZLibWorkQueue, ZPlayFabSocket> Sockets = new ConcurrentDictionary<PlayFabZLibWorkQueue, ZPlayFabSocket>();

		private static readonly FieldInfo WorkQueueField = AccessTools.Field(typeof(ZPlayFabSocket), "m_zlibWorkQueue");

		private static readonly FieldInfo WorkLockField = AccessTools.Field(typeof(PlayFabZLibWorkQueue), "m_lock");

		private static readonly FieldInfo WorkersField = AccessTools.Field(typeof(PlayFabZLibWorkQueue), "s_workers");

		private static readonly MethodInfo ExecuteMethod = AccessTools.Method(typeof(PlayFabZLibWorkQueue), "Execute", (Type[])null, (Type[])null);

		private static readonly MethodInfo LateUpdateMethod = AccessTools.Method(typeof(ZPlayFabSocket), "LateUpdate", (Type[])null, (Type[])null);

		internal static void SocketOpenPostfix(ZPlayFabSocket __instance, PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			if (__instance != null && ___m_zlibWorkQueue != null)
			{
				Sockets[___m_zlibWorkQueue] = __instance;
			}
		}

		internal static void SocketClosePostfix(PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			if (___m_zlibWorkQueue != null)
			{
				Sockets.TryRemove(___m_zlibWorkQueue, out var _);
			}
		}

		internal static bool CompressPrefix(PlayFabZLibWorkQueue __instance, Queue<byte[]> ___m_inCompress, Queue<byte[]> ___m_outCompress)
		{
			if (!Sockets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			if (!CompressionState.TryGet((ISocket)(object)value, out var state) || !state.Sending)
			{
				return true;
			}
			while (___m_inCompress.Count > 0)
			{
				byte[] input = ___m_inCompress.Dequeue();
				try
				{
					___m_outCompress.Enqueue(CompressionFrame.Encode(input));
				}
				catch (Exception ex)
				{
					BetterNetworking10.Log.Error("PlayFab compression failed: " + ex.Message);
				}
			}
			return false;
		}

		internal static bool DecompressPrefix(PlayFabZLibWorkQueue __instance, Queue<byte[]> ___m_inDecompress, Queue<byte[]> ___m_outDecompress)
		{
			if (!Sockets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			CompressionState.TryGet((ISocket)(object)value, out var state);
			while (___m_inDecompress.Count > 0)
			{
				byte[] array = ___m_inDecompress.Dequeue();
				bool flag = CompressionFrame.HasHeader(array);
				try
				{
					if (CompressionFrame.TryDecode(array, out var output))
					{
						___m_outDecompress.Enqueue(output);
						if (state != null && !state.Receiving)
						{
							state.Receiving = true;
							BetterNetworking10.Log.Warning("Received framed PlayFab packet before compression-start state; recovered automatically");
						}
						continue;
					}
				}
				catch (Exception ex)
				{
					BetterNetworking10.Log.Error("Invalid Better Networking PlayFab frame dropped: " + ex.Message);
					continue;
				}
				if (!flag)
				{
					try
					{
						byte[] item = ZlibStream.UncompressBuffer(array);
						___m_outDecompress.Enqueue(item);
					}
					catch
					{
						BetterNetworking10.Log.Warning("PlayFab packet was neither BN-framed nor vanilla-zlib; preserving raw data");
						___m_outDecompress.Enqueue(array);
					}
				}
			}
			return false;
		}

		internal static bool DirectCompressPrefix(PlayFabZLibWorkQueue __instance, byte[] payload, ref byte[] __result)
		{
			if (!Sockets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			if (!CompressionState.TryGet((ISocket)(object)value, out var state) || !state.Sending)
			{
				return true;
			}
			__result = CompressionFrame.Encode(payload);
			return false;
		}

		internal static bool DirectDecompressPrefix(PlayFabZLibWorkQueue __instance, byte[] payload, ref byte[] __result)
		{
			if (!Sockets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			if (!CompressionFrame.HasHeader(payload))
			{
				return true;
			}
			try
			{
				if (!CompressionFrame.TryDecode(payload, out __result))
				{
					return true;
				}
				if (CompressionState.TryGet((ISocket)(object)value, out var state) && !state.Receiving)
				{
					state.Receiving = true;
					BetterNetworking10.Log.Warning("Recovered an early/direct BN-compressed PlayFab packet");
				}
				return false;
			}
			catch
			{
				return true;
			}
		}

		internal static void Flush(ZPlayFabSocket socket)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if (socket == null || WorkQueueField == null || WorkLockField == null || WorkersField == null || ExecuteMethod == null || LateUpdateMethod == null)
			{
				return;
			}
			_ = (PlayFabZLibWorkQueue)WorkQueueField.GetValue(socket);
			object value = WorkLockField.GetValue(null);
			List<PlayFabZLibWorkQueue> list = (List<PlayFabZLibWorkQueue>)WorkersField.GetValue(null);
			lock (value)
			{
				PlayFabZLibWorkQueue[] array = list.ToArray();
				foreach (PlayFabZLibWorkQueue obj in array)
				{
					ExecuteMethod.Invoke(obj, Array.Empty<object>());
				}
			}
			LateUpdateMethod.Invoke(socket, null);
		}
	}
	internal static class ConnectionBufferPatch
	{
		private const int MaximumPacketsPerConnection = 256;

		private static readonly ConcurrentDictionary<ZRpc, List<ZPackage>> Buffers = new ConcurrentDictionary<ZRpc, List<ZPackage>>();

		private static readonly MethodInfo ReceiveZdoData = AccessTools.DeclaredMethod(typeof(ZDOMan), "RPC_ZDOData", (Type[])null, (Type[])null);

		internal static void OnNewConnectionPostfix(ZNet __instance, ZNetPeer peer)
		{
			if (!BetterNetworking10.ConnectionBufferActive || (Object)(object)__instance == (Object)null || peer?.m_rpc == null || __instance.IsServer())
			{
				return;
			}
			List<ZPackage> buffer = Buffers.GetOrAdd(peer.m_rpc, (ZRpc _) => new List<ZPackage>());
			peer.m_rpc.Register<ZPackage>("ZDOData", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage package)
			{
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Expected O, but got Unknown
				lock (buffer)
				{
					if (buffer.Count >= 256)
					{
						BetterNetworking10.Log.Error("Connection ZDO buffer reached 256 packets; dropping additional early packets");
					}
					else
					{
						buffer.Add(new ZPackage(package.GetArray()));
					}
				}
			});
		}

		internal static void AddPeerPostfix(ZDOMan __instance, ZNetPeer netPeer)
		{
			if (__instance == null || netPeer?.m_rpc == null || ReceiveZdoData == null || !Buffers.TryRemove(netPeer.m_rpc, out var value) || !BetterNetworking10.ConnectionBufferActive)
			{
				return;
			}
			lock (value)
			{
				BetterNetworking10.Log.Warning($"Replaying {value.Count} early ZDOData packet(s) for one connection");
				foreach (ZPackage item in value)
				{
					ReceiveZdoData.Invoke(__instance, new object[2] { netPeer.m_rpc, item });
				}
			}
		}

		internal static void ShutdownPostfix()
		{
			Clear();
		}

		internal static void Clear()
		{
			Buffers.Clear();
		}
	}
	internal static class DedicatedPatch
	{
		internal static void SetServerPrefix()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (BetterNetworking10.IsDedicated && !BetterNetworking10.SafeMode)
			{
				switch (BetterNetworking10.ForceCrossplay.Value)
				{
				case ForceCrossplayOption.PlayFab:
					ZNet.m_onlineBackend = (OnlineBackendType)1;
					break;
				case ForceCrossplayOption.Steamworks:
					ZNet.m_onlineBackend = (OnlineBackendType)0;
					break;
				}
			}
		}

		internal static int GetPlayerLimit()
		{
			return BetterNetworking10.SafeMode ? 10 : BetterNetworking10.PlayerLimit.Value;
		}

		internal static IEnumerable<CodeInstruction> PlayerLimitTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(ZNet), "GetNrOfPlayers", (Type[])null, (Type[])null);
			MethodInfo operand = AccessTools.DeclaredMethod(typeof(DedicatedPatch), "GetPlayerLimit", (Type[])null, (Type[])null);
			int num = -1;
			for (int i = 1; i < list.Count; i++)
			{
				if ((CodeInstructionExtensions.Is(list[i], OpCodes.Ldc_I4_S, (object)(sbyte)10) || CodeInstructionExtensions.Is(list[i], OpCodes.Ldc_I4, (object)10)) && CodeInstructionExtensions.Calls(list[i - 1], methodInfo))
				{
					if (num != -1)
					{
						BetterNetworking10.Log.Error("Player-limit transpiler found multiple guard constants; leaving RPC_PeerInfo unmodified");
						return list;
					}
					num = i;
				}
			}
			if (num == -1)
			{
				BetterNetworking10.Log.Error("Player-limit transpiler could not identify the 1.0 server-full guard; leaving RPC_PeerInfo unmodified");
				return list;
			}
			list[num].opcode = OpCodes.Call;
			list[num].operand = operand;
			BetterNetworking10.Log.Message($"Player-limit transpiler verified 1/1 IL match; limit={BetterNetworking10.PlayerLimit.Value}");
			return list;
		}
	}
	internal static class LegacyConfigMigration
	{
		private const string LegacyFileName = "CW_Jesse.BetterNetworking.cfg";

		internal static void TryImport(ConfigFile config, BNLog log)
		{
			string path = Path.Combine(Paths.ConfigPath, "CW_Jesse.BetterNetworking.cfg");
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				Dictionary<string, string> values = ReadValues(path);
				int num = 0;
				if (TryGet(values, "Networking", "Compression Enabled", out var value) && TryParseBoolean(value, out var result))
				{
					BetterNetworking10.EnableCompression.Value = result;
					num++;
				}
				if (TryGet(values, "Networking", "Queue Size", out var value2) && TryParseQueueSize(value2, out var result2))
				{
					BetterNetworking10.EnableQueueSize.Value = true;
					BetterNetworking10.QueueSize.Value = result2;
					num++;
				}
				if (TryGet(values, "Networking", "Update Rate", out var value3) && TryParseUpdateRate(value3, out var result3))
				{
					BetterNetworking10.EnableUpdateRate.Value = true;
					BetterNetworking10.UpdateRate.Value = result3;
					num++;
				}
				if (TryGet(values, "Networking (Steamworks)", "Minimum Send Rate", out var value4) && TryParseSendRate(value4, out var result4))
				{
					BetterNetworking10.EnableSendRate.Value = true;
					BetterNetworking10.MinimumSendRate.Value = result4;
					num++;
				}
				if (TryGet(values, "Networking (Steamworks)", "Maximum Send Rate", out var value5) && TryParseSendRate(value5, out var result5))
				{
					BetterNetworking10.EnableSendRate.Value = true;
					BetterNetworking10.MaximumSendRate.Value = result5;
					num++;
				}
				if (TryGet(values, "Dedicated Server", "Force Crossplay", out var value6) && TryParseCrossplay(value6, out var result6))
				{
					BetterNetworking10.ForceCrossplay.Value = result6;
					num++;
				}
				if (TryGet(values, "Dedicated Server", "Player Limit", out var value7) && int.TryParse(value7.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result7))
				{
					BetterNetworking10.PlayerLimit.Value = Math.Max(1, Math.Min(127, result7));
					num++;
				}
				if (TryGet(values, "Logging", "Log Level", out var value8) && TryParseLogLevel(value8, out var result8))
				{
					config.Bind<BNLog.LogLevel>("99 - Logging", "Log Level", BNLog.LogLevel.Message, (ConfigDescription)null).Value = result8;
					num++;
				}
				BetterNetworking10.EnableConnectionBuffer.Value = false;
				config.Save();
				log.Message(string.Format("Imported {0} setting(s) from legacy config {1}. The old file was left unchanged.", num, "CW_Jesse.BetterNetworking.cfg"));
			}
			catch (Exception ex)
			{
				log.Warning("Legacy config import skipped: " + ex.GetType().Name + ": " + ex.Message);
			}
		}

		private static Dictionary<string, string> ReadValues(string path)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			string section = string.Empty;
			string[] array = File.ReadAllLines(path);
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length == 0 || text2.StartsWith("#") || text2.StartsWith(";"))
				{
					continue;
				}
				if (text2.StartsWith("[") && text2.EndsWith("]"))
				{
					section = text2.Substring(1, text2.Length - 2).Trim();
					continue;
				}
				int num = text2.IndexOf('=');
				if (num > 0)
				{
					string key = text2.Substring(0, num).Trim();
					string value = text2.Substring(num + 1).Trim();
					dictionary[MakeKey(section, key)] = value;
				}
			}
			return dictionary;
		}

		private static bool TryGet(Dictionary<string, string> values, string section, string key, out string value)
		{
			return values.TryGetValue(MakeKey(section, key), out value);
		}

		private static string MakeKey(string section, string key)
		{
			return section + "\n" + key;
		}

		private static string Normalize(string value)
		{
			return new string(value.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
		}

		private static bool TryParseBoolean(string value, out bool result)
		{
			string text = Normalize(value);
			if (text == "true" || text == "enabled")
			{
				result = true;
				return true;
			}
			if (text == "false" || text == "disabled")
			{
				result = false;
				return true;
			}
			result = false;
			return false;
		}

		private static bool TryParseQueueSize(string value, out QueueSizeOption result)
		{
			string text = Normalize(value);
			if (text.Contains("80"))
			{
				result = QueueSizeOption.KB80;
			}
			else if (text.Contains("64"))
			{
				result = QueueSizeOption.KB64;
			}
			else if (text.Contains("48"))
			{
				result = QueueSizeOption.KB48;
			}
			else if (text.Contains("32"))
			{
				result = QueueSizeOption.KB32;
			}
			else
			{
				if (!text.Contains("vanilla") && !text.Contains("10"))
				{
					result = (QueueSizeOption)0;
					return false;
				}
				result = QueueSizeOption.Vanilla;
			}
			return true;
		}

		private static bool TryParseUpdateRate(string value, out UpdateRateOption result)
		{
			string text = Normalize(value);
			if (text.Contains("100"))
			{
				result = UpdateRateOption.Percent100;
			}
			else if (text.Contains("75"))
			{
				result = UpdateRateOption.Percent75;
			}
			else
			{
				if (!text.Contains("50"))
				{
					result = (UpdateRateOption)0;
					return false;
				}
				result = UpdateRateOption.Percent50;
			}
			return true;
		}

		private static bool TryParseSendRate(string value, out SendRateOption result)
		{
			string text = Normalize(value);
			if (text.Contains("1024"))
			{
				result = SendRateOption.KB1024;
			}
			else if (text.Contains("768"))
			{
				result = SendRateOption.KB768;
			}
			else if (text.Contains("512"))
			{
				result = SendRateOption.KB512;
			}
			else if (text.Contains("256"))
			{
				result = SendRateOption.KB256;
			}
			else
			{
				if (!text.Contains("150"))
				{
					result = (SendRateOption)0;
					return false;
				}
				result = SendRateOption.KB150;
			}
			return true;
		}

		private static bool TryParseCrossplay(string value, out ForceCrossplayOption result)
		{
			string text = Normalize(value);
			if (text.Contains("playfab") || text.Contains("enabled"))
			{
				result = ForceCrossplayOption.PlayFab;
			}
			else if (text.Contains("steamworks") || text.Contains("disabled"))
			{
				result = ForceCrossplayOption.Steamworks;
			}
			else
			{
				if (!text.Contains("vanilla"))
				{
					result = ForceCrossplayOption.Vanilla;
					return false;
				}
				result = ForceCrossplayOption.Vanilla;
			}
			return true;
		}

		private static bool TryParseLogLevel(string value, out BNLog.LogLevel result)
		{
			string text = Normalize(value);
			if (text.Contains("info"))
			{
				result = BNLog.LogLevel.Info;
			}
			else if (text.Contains("message"))
			{
				result = BNLog.LogLevel.Message;
			}
			else
			{
				if (!text.Contains("warning"))
				{
					result = BNLog.LogLevel.Warning;
					return false;
				}
				result = BNLog.LogLevel.Warning;
			}
			return true;
		}
	}
	internal static class QueueBudgetPatch
	{
		internal static int GetBudget()
		{
			return BetterNetworking10.QueueActive ? BetterNetworking10.QueueBudgetBytes : 10240;
		}

		internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			int num = list.Count((CodeInstruction i) => CodeInstructionExtensions.Is(i, OpCodes.Ldc_I4, (object)10240));
			if (num != 2)
			{
				BetterNetworking10.Log.Error($"Queue transpiler expected exactly 2 vanilla 10240 constants but found {num}; leaving SendZDOs unmodified");
				return list;
			}
			MethodInfo operand = AccessTools.DeclaredMethod(typeof(QueueBudgetPatch), "GetBudget", (Type[])null, (Type[])null);
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Is(item, OpCodes.Ldc_I4, (object)10240))
				{
					item.opcode = OpCodes.Call;
					item.operand = operand;
				}
			}
			BetterNetworking10.Log.Message($"Queue transpiler verified 2/2 IL matches; budget={BetterNetworking10.QueueBudgetBytes / 1024} KB");
			return list;
		}
	}
	internal static class UpdateRatePatch
	{
		internal static void Prefix(ref float dt)
		{
			if (BetterNetworking10.UpdateRateActive)
			{
				dt *= BetterNetworking10.UpdateRateMultiplier;
			}
		}
	}
	internal static class SteamSendRatePatch
	{
		private static bool _callbacksRegistered;

		private static bool _originalValuesCaptured;

		private static bool _customValuesApplied;

		private static int _originalMinimum;

		private static int _originalMaximum;

		internal static void Postfix()
		{
			_callbacksRegistered = true;
			CaptureOriginalValues();
			ApplyCurrent();
		}

		internal static void Refresh()
		{
			if (_callbacksRegistered)
			{
				ApplyCurrent();
			}
		}

		internal static void Clear()
		{
			if (_callbacksRegistered && _customValuesApplied && _originalValuesCaptured)
			{
				Set((ESteamNetworkingConfigValue)10, _originalMinimum, "restored");
				Set((ESteamNetworkingConfigValue)11, _originalMaximum, "restored");
			}
			_callbacksRegistered = false;
			_originalValuesCaptured = false;
			_customValuesApplied = false;
			_originalMinimum = 0;
			_originalMaximum = 0;
		}

		private static void ApplyCurrent()
		{
			if (!BetterNetworking10.SendRateActive)
			{
				if (_customValuesApplied && _originalValuesCaptured)
				{
					Set((ESteamNetworkingConfigValue)10, _originalMinimum, "restored");
					Set((ESteamNetworkingConfigValue)11, _originalMaximum, "restored");
				}
				_customValuesApplied = false;
				return;
			}
			int num = BetterNetworking10.RateBytesPerSecond(BetterNetworking10.MinimumSendRate.Value);
			int num2 = BetterNetworking10.RateBytesPerSecond(BetterNetworking10.MaximumSendRate.Value);
			if (num > num2)
			{
				int num3 = num;
				num = num2;
				num2 = num3;
				BetterNetworking10.Log.Warning("Steam send-rate minimum exceeded maximum; values were swapped at runtime");
			}
			Set((ESteamNetworkingConfigValue)10, num, "configured");
			Set((ESteamNetworkingConfigValue)11, num2, "configured");
			_customValuesApplied = true;
		}

		private static void CaptureOriginalValues()
		{
			if (!_originalValuesCaptured)
			{
				if (!TryGet((ESteamNetworkingConfigValue)10, out _originalMinimum) || !TryGet((ESteamNetworkingConfigValue)11, out _originalMaximum))
				{
					BetterNetworking10.Log.Warning("Could not capture the original Steamworks send-rate limits; live disabling will leave the latest configured values in place until restart");
				}
				else
				{
					_originalValuesCaptured = true;
				}
			}
		}

		private static bool TryGet(ESteamNetworkingConfigValue key, out int value)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Invalid comparison between Unknown and I4
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Invalid comparison between Unknown and I4
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Invalid comparison between Unknown and I4
			value = 0;
			ulong num = 4uL;
			byte[] value2 = new byte[4];
			GCHandle gCHandle = GCHandle.Alloc(value2, GCHandleType.Pinned);
			try
			{
				ESteamNetworkingConfigDataType val2 = default(ESteamNetworkingConfigDataType);
				ESteamNetworkingGetConfigValueResult val = (BetterNetworking10.IsDedicated ? SteamGameServerNetworkingUtils.GetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, ref val2, gCHandle.AddrOfPinnedObject(), ref num) : SteamNetworkingUtils.GetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, ref val2, gCHandle.AddrOfPinnedObject(), ref num));
				if (((int)val != 1 && (int)val != 2) || (int)val2 != 1 || num < 4)
				{
					return false;
				}
				value = BitConverter.ToInt32(value2, 0);
				return true;
			}
			catch (Exception ex)
			{
				BetterNetworking10.Log.Warning($"Could not read Steamworks {key}: {ex.Message}");
				return false;
			}
			finally
			{
				gCHandle.Free();
			}
		}

		private static void Set(ESteamNetworkingConfigValue key, int value, string action)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
			try
			{
				if (BetterNetworking10.IsDedicated)
				{
					SteamGameServerNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject());
				}
				else
				{
					SteamNetworkingUtils.SetConfigValue(key, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject());
				}
				BetterNetworking10.Log.Message($"Steamworks {key} {action}: {value / 1024} KB/s");
			}
			catch (Exception ex)
			{
				BetterNetworking10.Log.Error($"Could not set Steamworks {key}: {ex.Message}");
			}
			finally
			{
				gCHandle.Free();
			}
		}
	}
	internal static class PortalGhostRefresh
	{
		private const string StandalonePluginGuid = "sfantul.portalobserverfix";

		private const float CheckIntervalSeconds = 0.25f;

		private const float DelayedRefreshSeconds = 0.75f;

		private const float MinimumJumpDistance = 64f;

		private const float MinimumJumpDistanceSquared = 4096f;

		private static readonly Dictionary<ZDOID, Vector3> LastPositions = new Dictionary<ZDOID, Vector3>();

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

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

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

		private static ZDOMan _manager;

		private static float _nextCheck;

		private static bool _externalModInstalled;

		private static bool _runtimeDisabled;

		internal static void Initialize()
		{
			_externalModInstalled = Chainloader.PluginInfos.ContainsKey("sfantul.portalobserverfix");
			_runtimeDisabled = false;
			if (!BetterNetworking10.PortalGhostFixActive)
			{
				BetterNetworking10.Log.Message("FEATURE SKIP Portal ghost fix: disabled by configuration");
			}
			else if (_externalModInstalled)
			{
				BetterNetworking10.Log.Warning("FEATURE SKIP Portal ghost fix: standalone Sfantul-PortalGhostFix detected; built-in refresh disabled to avoid duplicate sends");
			}
			else
			{
				BetterNetworking10.Log.Message("FEATURE ON Portal ghost fix: server-side player and linked status-effect refresh; vanilla clients supported");
			}
		}

		internal static void Tick()
		{
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_026b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Unknown result type (might be due to invalid IL or missing references)
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			if (!BetterNetworking10.PortalGhostFixActive || _externalModInstalled || _runtimeDisabled)
			{
				return;
			}
			ZNet instance = ZNet.instance;
			ZDOMan instance2 = ZDOMan.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer() || instance2 == null)
			{
				if (_manager != null)
				{
					ClearRuntimeState();
				}
				return;
			}
			if (_manager != instance2)
			{
				ClearRuntimeState();
				_manager = instance2;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextCheck)
			{
				return;
			}
			_nextCheck = realtimeSinceStartup + 0.25f;
			try
			{
				ActivePlayers.Clear();
				foreach (PlayerInfo player in instance.GetPlayerList())
				{
					ZDOID characterID = player.m_characterID;
					if (characterID == ZDOID.None)
					{
						continue;
					}
					ActivePlayers.Add(characterID);
					ZDO zDO = instance2.GetZDO(characterID);
					if (zDO == null)
					{
						LastPositions.Remove(characterID);
						DelayedRefreshes.Remove(characterID);
						continue;
					}
					Vector3 position = zDO.GetPosition();
					if (LastPositions.TryGetValue(characterID, out var value))
					{
						Vector3 val = position - value;
						if (((Vector3)(ref val)).sqrMagnitude >= 4096f)
						{
							int num = ForceRefresh(instance2, characterID);
							DelayedRefreshes[characterID] = realtimeSinceStartup + 0.75f;
							BetterNetworking10.Log.Message($"Portal ghost refresh queued for {SafePlayerName(player.m_name)} ({characterID}) after {((Vector3)(ref val)).magnitude:F1} m movement; linked effects={num}");
						}
					}
					if (DelayedRefreshes.TryGetValue(characterID, out var value2) && realtimeSinceStartup >= value2)
					{
						ForceRefresh(instance2, characterID);
						DelayedRefreshes.Remove(characterID);
					}
					LastPositions[characterID] = position;
				}
				TemporaryIds.Clear();
				foreach (ZDOID key in LastPositions.Keys)
				{
					if (!ActivePlayers.Contains(key))
					{
						TemporaryIds.Add(key);
					}
				}
				foreach (ZDOID temporaryId in TemporaryIds)
				{
					LastPositions.Remove(temporaryId);
					DelayedRefreshes.Remove(temporaryId);
				}
			}
			catch (Exception ex)
			{
				_runtimeDisabled = true;
				ClearRuntimeState();
				BetterNetworking10.Log.Error("FEATURE FAILED Portal ghost fix: " + ex.GetType().Name + ": " + ex.Message + ". Disabled until restart; networking remains otherwise unchanged");
			}
		}

		internal static void Clear()
		{
			ClearRuntimeState();
			_externalModInstalled = false;
			_runtimeDisabled = false;
		}

		private static int ForceRefresh(ZDOMan manager, ZDOID characterId)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			manager.ForceSendZDO(characterId);
			int num = 0;
			List<ZDOID> allZDOIDsWithHash = ZDOExtraData.GetAllZDOIDsWithHash((Type)4, ZDOVars.s_ownerZDOUserId);
			foreach (ZDOID item in allZDOIDsWithHash)
			{
				if (!(item == characterId))
				{
					ZDO zDO = manager.GetZDO(item);
					if (zDO != null && zDO.GetLong(ZDOVars.s_ownerZDOUserId, 0L) == ((ZDOID)(ref characterId)).UserID && zDO.GetLong(ZDOVars.s_ownerZDOId, 0L) == ((ZDOID)(ref characterId)).ID)
					{
						manager.ForceSendZDO(item);
						num++;
					}
				}
			}
			return num;
		}

		private static void ClearRuntimeState()
		{
			LastPositions.Clear();
			DelayedRefreshes.Clear();
			ActivePlayers.Clear();
			TemporaryIds.Clear();
			_manager = null;
			_nextCheck = 0f;
		}

		private static string SafePlayerName(string name)
		{
			return string.IsNullOrEmpty(name) ? "unknown player" : name;
		}
	}
	internal static class VersionInfo
	{
		public const string Version = "1.3.1";
	}
}
namespace Costura
{
	[CompilerGenerated]
	internal static class AssemblyLoader
	{
		private static object nullCacheLock = new object();

		private static Dictionary<string, bool> nullCache = new Dictionary<string, bool>();

		private static Dictionary<string, string> assemblyNames = new Dictionary<string, string>();

		private static Dictionary<string, string> symbolNames = new Dictionary<string, string>();

		private static int isAttached;

		private static string CultureToString(CultureInfo culture)
		{
			if (culture == null)
			{
				return "";
			}
			return culture.Name;
		}

		private static Assembly ReadExistingAssembly(AssemblyName name)
		{
			AppDomain currentDomain = AppDomain.CurrentDomain;
			Assembly[] assemblies = currentDomain.GetAssemblies();
			Assembly[] array = assemblies;
			foreach (Assembly assembly in array)
			{
				AssemblyName name2 = assembly.GetName();
				if (string.Equals(name2.Name, name.Name, StringComparison.InvariantCultureIgnoreCase) && string.Equals(CultureToString(name2.CultureInfo), CultureToString(name.CultureInfo), StringComparison.InvariantCultureIgnoreCase))
				{
					return assembly;
				}
			}
			return null;
		}

		private static void CopyTo(Stream source, Stream destination)
		{
			byte[] array = new byte[81920];
			int count;
			while ((count = source.Read(array, 0, array.Length)) != 0)
			{
				destination.Write(array, 0, count);
			}
		}

		private static Stream LoadStream(string fullName)
		{
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			if (fullName.EndsWith(".compressed"))
			{
				using (Stream stream = executingAssembly.GetManifestResourceStream(fullName))
				{
					using DeflateStream source = new DeflateStream(stream, CompressionMode.Decompress);
					MemoryStream memoryStream = new MemoryStream();
					CopyTo(source, memoryStream);
					memoryStream.Position = 0L;
					return memoryStream;
				}
			}
			return executingAssembly.GetManifestResourceStream(fullName);
		}

		private static Stream LoadStream(Dictionary<string, string> resourceNames, string name)
		{
			if (resourceNames.TryGetValue(name, out var value))
			{
				return LoadStream(value);
			}
			return null;
		}

		private static byte[] ReadStream(Stream stream)
		{
			byte[] array = new byte[stream.Length];
			stream.Read(array, 0, array.Length);
			return array;
		}

		private static Assembly ReadFromEmbeddedResources(Dictionary<string, string> assemblyNames, Dictionary<string, string> symbolNames, AssemblyName requestedAssemblyName)
		{
			string text = requestedAssemblyName.Name.ToLowerInvariant();
			if (requestedAssemblyName.CultureInfo != null && !string.IsNullOrEmpty(requestedAssemblyName.CultureInfo.Name))
			{
				text = requestedAssemblyName.CultureInfo.Name + "." + text;
			}
			byte[] rawAssembly;
			using (Stream stream = LoadStream(assemblyNames, text))
			{
				if (stream == null)
				{
					return null;
				}
				rawAssembly = ReadStream(stream);
			}
			using (Stream stream2 = LoadStream(symbolNames, text))
			{
				if (stream2 != null)
				{
					byte[] rawSymbolStore = ReadStream(stream2);
					return Assembly.Load(rawAssembly, rawSymbolStore);
				}
			}
			return Assembly.Load(rawAssembly);
		}

		public static Assembly ResolveAssembly(object sender, ResolveEventArgs e)
		{
			lock (nullCacheLock)
			{
				if (nullCache.ContainsKey(e.Name))
				{
					return null;
				}
			}
			AssemblyName assemblyName = new AssemblyName(e.Name);
			Assembly assembly = ReadExistingAssembly(assemblyName);
			if ((object)assembly != null)
			{
				return assembly;
			}
			assembly = ReadFromEmbeddedResources(assemblyNames, symbolNames, assemblyName);
			if ((object)assembly == null)
			{
				lock (nullCacheLock)
				{
					nullCache[e.Name] = true;
				}
				if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != AssemblyNameFlags.None)
				{
					assembly = Assembly.Load(assemblyName);
				}
			}
			return assembly;
		}

		static AssemblyLoader()
		{
			assemblyNames.Add("costura", "costura.costura.dll.compressed");
			symbolNames.Add("costura", "costura.costura.pdb.compressed");
			assemblyNames.Add("system.buffers", "costura.system.buffers.dll.compressed");
			assemblyNames.Add("system.diagnostics.diagnosticsource", "costura.system.diagnostics.diagnosticsource.dll.compressed");
			assemblyNames.Add("system.memory", "costura.system.memory.dll.compressed");
			assemblyNames.Add("system.numerics.vectors", "costura.system.numerics.vectors.dll.compressed");
			assemblyNames.Add("system.runtime.compilerservices.unsafe", "costura.system.runtime.compilerservices.unsafe.dll.compressed");
			assemblyNames.Add("system.threading.tasks.extensions", "costura.system.threading.tasks.extensions.dll.compressed");
			assemblyNames.Add("zstdsharp", "costura.zstdsharp.dll.compressed");
		}

		public static void Attach()
		{
			if (Interlocked.Exchange(ref isAttached, 1) == 1)
			{
				return;
			}
			AppDomain currentDomain = AppDomain.CurrentDomain;
			currentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
			{
				lock (nullCacheLock)
				{
					if (nullCache.ContainsKey(e.Name))
					{
						return (Assembly)null;
					}
				}
				AssemblyName assemblyName = new AssemblyName(e.Name);
				Assembly assembly = ReadExistingAssembly(assemblyName);
				if ((object)assembly != null)
				{
					return assembly;
				}
				assembly = ReadFromEmbeddedResources(assemblyNames, symbolNames, assemblyName);
				if ((object)assembly == null)
				{
					lock (nullCacheLock)
					{
						nullCache[e.Name] = true;
					}
					if ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != AssemblyNameFlags.None)
					{
						assembly = Assembly.Load(assemblyName);
					}
				}
				return assembly;
			};
		}
	}
}
internal class DITBetterNetworking10_ProcessedByFody
{
	internal const string FodyVersion = "6.9.3.0";

	internal const string Costura = "5.7.0";
}