Decompiled source of BetterNetworking Valheim v2.3.2

CW_Jesse.BetterNetworking.dll

Decompiled 6 months ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
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.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Costura;
using HarmonyLib;
using Steamworks;
using UnityEngine;
using ZstdSharp;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CW_Jesse")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("CW_Jesse 2023")]
[assembly: AssemblyFileVersion("2.3.2")]
[assembly: AssemblyInformationalVersion("2.3.2")]
[assembly: AssemblyProduct("CW_Jesse.BetterNetworking")]
[assembly: AssemblyTitle("CW_Jesse.BetterNetworking")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/CW-Jesse/valheim-betternetworking")]
[assembly: AssemblyVersion("2.3.2.0")]
internal class <Module>
{
	static <Module>()
	{
		AssemblyLoader.Attach();
	}
}
namespace CW_Jesse.BetterNetworking
{
	[BepInPlugin("CW_Jesse.BetterNetworking", "Better Networking", "2.3.2")]
	[BepInIncompatibility("org.bepinex.plugins.network")]
	[BepInIncompatibility("be.sebastienvercammen.valheim.netcompression")]
	[BepInIncompatibility("com.github.dalayeth.Networkfix")]
	[BepInIncompatibility("Steel.ValheimMod")]
	public class BetterNetworking : BaseUnityPlugin
	{
		private readonly Harmony harmony = new Harmony("CW_Jesse.BetterNetworking");

		public static ConfigEntry<BN_Logger.Options_Logger_LogLevel> configLogMessages;

		public static ConfigEntry<BN_Patch_ForceCrossplay.Options_ForceCrossplay> configForceCrossplay;

		public static ConfigEntry<int> configPlayerLimit;

		public static ConfigEntry<BN_Patch_Compression.Options_NetworkCompression> configCompressionEnabled;

		public static ConfigEntry<BN_Patch_UpdateRate.Options_NetworkUpdateRates> configNetworkUpdateRate;

		public static ConfigEntry<BN_Patch_SendRate.Options_NetworkSendRateMin> configNetworkSendRateMin;

		public static ConfigEntry<BN_Patch_SendRate.Options_NetworkSendRateMax> configNetworkSendRateMax;

		public static ConfigEntry<BN_Patch_QueueSize.Options_NetworkQueueSize> configNetworkQueueSize;

		private void Awake()
		{
			BN_Logger.Init(((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Config);
			BN_Patch_Compression.InitCompressor();
			BN_Patch_Compression.InitConfig(((BaseUnityPlugin)this).Config);
			BN_Patch_UpdateRate.InitConfig(((BaseUnityPlugin)this).Config);
			BN_Patch_SendRate.InitConfig(((BaseUnityPlugin)this).Config);
			BN_Patch_QueueSize.InitConfig(((BaseUnityPlugin)this).Config);
			BN_Patch_DedicatedServer.InitConfig(((BaseUnityPlugin)this).Config);
			harmony.PatchAll();
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}
	}
	[HarmonyPatch]
	public class BN_Patch_DedicatedServer
	{
		private static ConfigFile config;

		public static void InitConfig(ConfigFile configFile)
		{
			config = configFile;
		}

		[HarmonyPatch(typeof(ZNet), "IsDedicated")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> DedicatedServerInit(IEnumerable<CodeInstruction> instructions)
		{
			bool isDedicated = false;
			foreach (CodeInstruction instruction in instructions)
			{
				if (instruction.opcode == OpCodes.Ldc_I4_1)
				{
					isDedicated = true;
					BN_Patch_ForceCrossplay.InitConfig(config);
					BN_Patch_ChangePlayerLimit.InitConfig(config);
				}
			}
			BN_Utils.isDedicated = isDedicated;
			return instructions;
		}
	}
	public class BN_Logger
	{
		public enum Options_Logger_LogLevel
		{
			[Description("Errors/Warnings")]
			warning,
			[Description("Errors/Warnings/Messages <b>[default]</b>")]
			message,
			[Description("Errors/Warnings/Messages/Info")]
			info
		}

		private static ManualLogSource logger;

		public static void Init(ManualLogSource logger, ConfigFile config)
		{
			BN_Logger.logger = logger;
			BetterNetworking.configLogMessages = config.Bind<Options_Logger_LogLevel>("Logging", "Log Level", Options_Logger_LogLevel.message, "Better Network's verbosity in console/logs.");
		}

		public static void LogError(object data)
		{
			logger.LogError(data);
		}

		public static void LogWarning(object data)
		{
			logger.LogWarning(data);
		}

		public static void LogMessage(object data)
		{
			if (BetterNetworking.configLogMessages.Value >= Options_Logger_LogLevel.message)
			{
				logger.LogMessage(data);
			}
		}

		public static void LogInfo(object data)
		{
			if (BetterNetworking.configLogMessages.Value >= Options_Logger_LogLevel.info)
			{
				logger.LogInfo(data);
			}
		}
	}
	internal class BN_Utils
	{
		public static bool isDedicated;

		public static ZNetPeer GetPeer(ZRpc rpc)
		{
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					return peer;
				}
			}
			return null;
		}

		public static ZNetPeer GetPeer(ISocket socket)
		{
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_socket == socket)
				{
					return peer;
				}
			}
			return null;
		}

		public static string GetPeerName(ZNetPeer peer)
		{
			if (peer == null)
			{
				return "[null]";
			}
			if (peer.m_server)
			{
				return "[server]";
			}
			return peer.m_playerName + "[" + peer.m_socket.GetHostName() + "]";
		}

		public static string GetPeerName(ISocket socket)
		{
			return GetPeerName(GetPeer(socket));
		}
	}
	[HarmonyPatch]
	public class BN_Patch_ChangePlayerLimit
	{
		public static void InitConfig(ConfigFile config)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			BetterNetworking.configPlayerLimit = config.Bind<int>("Dedicated Server", "Player Limit", 10, new ConfigDescription("Requires restart. Changes player limit for dedicated servers.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 127), Array.Empty<object>()));
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> SetPlayerLimit(IEnumerable<CodeInstruction> instructions)
		{
			foreach (CodeInstruction instruction in instructions)
			{
				if (BN_Utils.isDedicated && CodeInstructionExtensions.Is(instruction, OpCodes.Ldc_I4_S, (object)(sbyte)10))
				{
					yield return new CodeInstruction(OpCodes.Ldc_I4_S, (object)(sbyte)BetterNetworking.configPlayerLimit.Value);
				}
				else
				{
					yield return instruction;
				}
			}
		}
	}
	[HarmonyPatch]
	public class BN_Patch_ForceCrossplay
	{
		public enum Options_ForceCrossplay
		{
			[Description("Vanilla behaviour <b>[default]</b>")]
			vanilla,
			[Description("Crossplay ENABLED")]
			playfab,
			[Description("Crossplay DISABLED")]
			steamworks
		}

		public static void InitConfig(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			BetterNetworking.configForceCrossplay = config.Bind<Options_ForceCrossplay>("Dedicated Server", "Force Crossplay", Options_ForceCrossplay.vanilla, new ConfigDescription("Requires restart.\nplayfab (crossplay enabled): Forces dedicated servers to use new PlayFab networking stack.\nsteamworks (crossplay disabled): Forces dedicated servers to use Steamworks network stack.\nvanilla: Listen for -crossplay flag.", (AcceptableValueBase)null, Array.Empty<object>()));
		}

		[HarmonyPatch(typeof(FejdStartup), "ParseServerArguments")]
		[HarmonyPostfix]
		private static void ForceCrossplay()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if (BN_Utils.isDedicated)
			{
				if (BetterNetworking.configForceCrossplay.Value == Options_ForceCrossplay.playfab)
				{
					ZNet.m_onlineBackend = (OnlineBackendType)1;
					ZPlayFabMatchmaking.LookupPublicIP();
				}
				if (BetterNetworking.configForceCrossplay.Value == Options_ForceCrossplay.steamworks)
				{
					ZNet.m_onlineBackend = (OnlineBackendType)0;
				}
			}
		}
	}
	[HarmonyPatch]
	public class BN_Patch_NewConnectionBuffer
	{
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private class StartBufferingOnNewConnection
		{
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				if (!__instance.IsServer())
				{
					peer.m_rpc.Register<ZPackage>("ZDOData", (Action<ZRpc, ZPackage>)delegate(ZRpc nullPeer, ZPackage package)
					{
						packageBuffer.Add(package);
					});
				}
			}
		}

		[HarmonyPatch(typeof(ZDOMan), "AddPeer")]
		private class SendBufferOnAddPeer
		{
			private static void Postfix(ZDOMan __instance, ZNetPeer netPeer)
			{
				if (packageBuffer.Count <= 0)
				{
					return;
				}
				BN_Logger.LogWarning($"Connection buffer: Sending {packageBuffer.Count} buffered packages; Valheim or a mod is trying to send data too early");
				foreach (ZPackage item in packageBuffer)
				{
					AccessTools.Method(typeof(ZDOMan), "RPC_ZDOData", (Type[])null, (Type[])null).Invoke(__instance, new object[2] { netPeer.m_rpc, item });
				}
				packageBuffer.Clear();
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private class ClearBufferOnShutdown
		{
			private static void Postfix()
			{
				packageBuffer.Clear();
			}
		}

		private static readonly List<ZPackage> packageBuffer = new List<ZPackage>();
	}
	[HarmonyPatch]
	public class BN_Patch_QueueSize
	{
		public enum Options_NetworkQueueSize
		{
			[Description("80 KB")]
			_80KB,
			[Description("64 KB")]
			_64KB,
			[Description("48 KB")]
			_48KB,
			[Description("32 KB <b>[default]</b>")]
			_32KB,
			[Description("[Valheim default]")]
			_vanilla
		}

		private const int DEFAULT_QUEUE_SIZE = 10240;

		private const int DEFAULT_MINIMUM_QUEUE_SIZE = 2048;

		public static void InitConfig(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			BetterNetworking.configNetworkQueueSize = config.Bind<Options_NetworkQueueSize>("Networking", "Queue Size", Options_NetworkQueueSize._32KB, new ConfigDescription("The better your upload speed, the higher you can set this.\nHigher options aren't available as they can cause errors in Steam.\nWith compression and 100% update rate, 32 KB spikes upload speeds to 256 KB/s. (32KB*0.4*20/s)---\nIf others experience lag/desync for things <i>around</i> you, increase your queue size.\nIf your <i>character</i> is lagging for others, decrease your update rate and/or queue size.", (AcceptableValueBase)null, Array.Empty<object>()));
		}

		[HarmonyPatch(typeof(ZSteamSocket), "GetSendQueueSize")]
		[HarmonyPostfix]
		private static void Steamworks_GetSendQueueSize(ref int __result)
		{
			switch (BetterNetworking.configNetworkQueueSize.Value)
			{
			case Options_NetworkQueueSize._80KB:
				__result -= 71680;
				break;
			case Options_NetworkQueueSize._64KB:
				__result -= 55296;
				break;
			case Options_NetworkQueueSize._48KB:
				__result -= 38912;
				break;
			case Options_NetworkQueueSize._32KB:
				__result -= 22528;
				break;
			}
		}

		[HarmonyPatch(typeof(ZPlayFabSocket), "GetSendQueueSize")]
		[HarmonyPrefix]
		private static bool PlayFab_GetSendQueueSize(ref int __result, ref InFlightQueue ___m_inFlightQueue)
		{
			switch (BetterNetworking.configNetworkQueueSize.Value)
			{
			case Options_NetworkQueueSize._80KB:
				__result = (int)(___m_inFlightQueue.Bytes - 71680);
				return false;
			case Options_NetworkQueueSize._64KB:
				__result = (int)(___m_inFlightQueue.Bytes - 55296);
				return false;
			case Options_NetworkQueueSize._48KB:
				__result = (int)(___m_inFlightQueue.Bytes - 38912);
				return false;
			case Options_NetworkQueueSize._32KB:
				__result = (int)(___m_inFlightQueue.Bytes - 22528);
				return false;
			default:
				return true;
			}
		}
	}
	[HarmonyPatch]
	public class BN_Patch_SendRate
	{
		public enum Options_NetworkSendRateMin
		{
			[Description("1024 KB/s | 8 Mbit/s")]
			_1024KB,
			[Description("768 KB/s | 6 Mbit/s")]
			_768KB,
			[Description("512 KB/s | 4 Mbit/s")]
			_512KB,
			[Description("256 KB/s | 2 Mbit/s <b>[default]</b>")]
			_256KB,
			[Description("150 KB/s | 1.2 Mbit/s [Valheim default]")]
			_150KB
		}

		public enum Options_NetworkSendRateMax
		{
			[Description("1024 KB/s | 8 Mbit/s <b>[default]</b>")]
			_1024KB,
			[Description("768 KB/s | 6 Mbit/s")]
			_768KB,
			[Description("512 KB/s | 4 Mbit/s")]
			_512KB,
			[Description("256 KB/s | 2 Mbit/s")]
			_256KB,
			[Description("150 KB/s | 1.2 Mbit/s [Valheim default]")]
			_150KB
		}

		[HarmonyPatch(typeof(SteamNetworkingUtils))]
		[HarmonyPatch(typeof(SteamGameServerNetworkingUtils))]
		private class NetworkSendRate_Patch
		{
			public static int SendRateMin => BetterNetworking.configNetworkSendRateMin.Value switch
			{
				Options_NetworkSendRateMin._1024KB => 1048576, 
				Options_NetworkSendRateMin._768KB => 786432, 
				Options_NetworkSendRateMin._512KB => 524288, 
				Options_NetworkSendRateMin._256KB => 262144, 
				_ => 153600, 
			};

			public static int SendRateMax => BetterNetworking.configNetworkSendRateMax.Value switch
			{
				Options_NetworkSendRateMax._1024KB => 1048576, 
				Options_NetworkSendRateMax._768KB => 786432, 
				Options_NetworkSendRateMax._512KB => 524288, 
				Options_NetworkSendRateMax._256KB => 262144, 
				_ => 153600, 
			};

			public static void SetSendRateMinFromConfig()
			{
				SetSteamNetworkConfig((ESteamNetworkingConfigValue)10, SendRateMin);
			}

			public static void SetSendRateMaxFromConfig()
			{
				SetSteamNetworkConfig((ESteamNetworkingConfigValue)11, SendRateMax);
			}

			public static void SetSendBufferSize()
			{
				SetSteamNetworkConfig((ESteamNetworkingConfigValue)9, 524288);
			}

			private static int GetSteamNetworkConfig(ESteamNetworkingConfigValue valueType)
			{
				//IL_0078: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0058: Unknown result type (might be due to invalid IL or missing references)
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: 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)
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					BN_Logger.LogInfo($"Steamworks: Unable to get net config while disconnected: {valueType}");
					return -1;
				}
				ulong num = 4uL;
				byte[] value = new byte[num];
				GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
				try
				{
					ESteamNetworkingConfigDataType val = default(ESteamNetworkingConfigDataType);
					if (BN_Utils.isDedicated)
					{
						SteamGameServerNetworkingUtils.GetConfigValue(valueType, (ESteamNetworkingConfigScope)1, IntPtr.Zero, ref val, gCHandle.AddrOfPinnedObject(), ref num);
					}
					else
					{
						SteamNetworkingUtils.GetConfigValue(valueType, (ESteamNetworkingConfigScope)1, IntPtr.Zero, ref val, gCHandle.AddrOfPinnedObject(), ref num);
					}
				}
				catch
				{
					BN_Logger.LogError($"Steamworks: Unable to get net config: {valueType}");
				}
				gCHandle.Free();
				return BitConverter.ToInt32(value, 0);
			}

			private static void SetSteamNetworkConfig(ESteamNetworkingConfigValue valueType, int value)
			{
				//IL_0072: 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_0012: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0055: Unknown result type (might be due to invalid IL or missing references)
				//IL_003e: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					BN_Logger.LogInfo($"Steamworks: Unable to set net config while disconnected: {valueType}");
					return;
				}
				int steamNetworkConfig = GetSteamNetworkConfig(valueType);
				GCHandle gCHandle = GCHandle.Alloc(value, GCHandleType.Pinned);
				try
				{
					if (BN_Utils.isDedicated)
					{
						SteamGameServerNetworkingUtils.SetConfigValue(valueType, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject());
					}
					else
					{
						SteamNetworkingUtils.SetConfigValue(valueType, (ESteamNetworkingConfigScope)1, IntPtr.Zero, (ESteamNetworkingConfigDataType)1, gCHandle.AddrOfPinnedObject());
					}
				}
				catch
				{
					BN_Logger.LogError($"Steamworks: Unable to set net config: {valueType}");
				}
				gCHandle.Free();
				BN_Logger.LogMessage($"Steamworks: {valueType}: {steamNetworkConfig} -> {GetSteamNetworkConfig(valueType)} (attempted {value})");
			}

			[HarmonyPatch("SetConfigValue")]
			private static void Prefix(ESteamNetworkingConfigValue eValue, ESteamNetworkingConfigScope eScopeType, IntPtr scopeObj, ESteamNetworkingConfigDataType eDataType, ref IntPtr pArg)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				BN_Logger.LogInfo($"Steamworks: {eValue}: {GetSteamNetworkConfig(eValue)} -> {Marshal.ReadInt32(pArg)}");
			}
		}

		[HarmonyPatch(typeof(ZSteamSocket), "RegisterGlobalCallbacks")]
		private class PreventValheimControlOfNetworkRate_Patch
		{
			private static void Postfix()
			{
				NetworkSendRate_Patch.SetSendRateMinFromConfig();
				NetworkSendRate_Patch.SetSendRateMaxFromConfig();
			}
		}

		private const int DEFAULT_SEND_BUFFER_SIZE = 524288;

		private const int SEND_BUFFER_SIZE = 524288;

		public static void InitConfig(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			BetterNetworking.configNetworkSendRateMin = config.Bind<Options_NetworkSendRateMin>("Networking (Steamworks)", "Minimum Send Rate", Options_NetworkSendRateMin._256KB, new ConfigDescription("Steamworks: The minimum speed Steam will <i>attempt</i> to send data.\n<b>Lower this below your internet upload speed.</b>\n", (AcceptableValueBase)null, Array.Empty<object>()));
			BetterNetworking.configNetworkSendRateMax = config.Bind<Options_NetworkSendRateMax>("Networking (Steamworks)", "Maximum Send Rate", Options_NetworkSendRateMax._1024KB, new ConfigDescription("Steamworks: The maximum speed Steam will <i>attempt</i> to send data.\nIf you have a low upload speed, lower this <i>below</i> your internet upload speed.\n", (AcceptableValueBase)null, Array.Empty<object>()));
			ConfigNetworkSendRateSettings_Listen();
		}

		public static void ConfigNetworkSendRateSettings_Listen()
		{
			BetterNetworking.configNetworkSendRateMin.SettingChanged += ConfigNetworkSendRateMin_SettingChanged;
			BetterNetworking.configNetworkSendRateMax.SettingChanged += ConfigNetworkSendRateMax_SettingChanged;
		}

		private static void ConfigNetworkSendRateMin_SettingChanged(object sender, EventArgs e)
		{
			if ((int)(BetterNetworking.configNetworkSendRateMin.Value + 1) < (int)BetterNetworking.configNetworkSendRateMax.Value)
			{
				BetterNetworking.configNetworkSendRateMax.Value = (Options_NetworkSendRateMax)(BetterNetworking.configNetworkSendRateMin.Value + 1);
			}
			NetworkSendRate_Patch.SetSendRateMinFromConfig();
		}

		private static void ConfigNetworkSendRateMax_SettingChanged(object sender, EventArgs e)
		{
			if ((int)BetterNetworking.configNetworkSendRateMax.Value > (int)(BetterNetworking.configNetworkSendRateMin.Value + 1))
			{
				BetterNetworking.configNetworkSendRateMin.Value = (Options_NetworkSendRateMin)(BetterNetworking.configNetworkSendRateMax.Value - 1);
			}
			NetworkSendRate_Patch.SetSendRateMaxFromConfig();
		}
	}
	public class BN_Patch_UpdateRate
	{
		public enum Options_NetworkUpdateRates
		{
			[Description("100% <b>[default]</b>")]
			_100,
			[Description("75%")]
			_75,
			[Description("50%")]
			_50
		}

		[HarmonyPatch(typeof(ZDOMan))]
		private class NetworkUpdateFrequency_Patch
		{
			[HarmonyPatch("SendZDOToPeers2")]
			private static void Prefix(ref float dt)
			{
				switch (BetterNetworking.configNetworkUpdateRate.Value)
				{
				case Options_NetworkUpdateRates._75:
					dt *= 0.75f;
					break;
				case Options_NetworkUpdateRates._50:
					dt *= 0.5f;
					break;
				}
			}
		}

		public static void InitConfig(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			BetterNetworking.configNetworkUpdateRate = config.Bind<Options_NetworkUpdateRates>("Networking", "Update Rate", Options_NetworkUpdateRates._100, new ConfigDescription("Reducing this can help if your upload speed is low.\n100%: 20 updates/second\n75%: 15 updates/second\n50%: 10 updates/second", (AcceptableValueBase)null, Array.Empty<object>()));
		}
	}
	[HarmonyPatch]
	public class BN_Patch_Compression
	{
		public enum Options_NetworkCompression
		{
			[Description("Enabled <b>[default]</b>")]
			@true,
			[Description("Disabled")]
			@false
		}

		private static string ZSTD_DICT_RESOURCE_NAME = "CW_Jesse.BetterNetworking.dict.small";

		private static int ZSTD_LEVEL = 1;

		private static Compressor compressor;

		private static Decompressor decompressor;

		private const string RPC_COMPRESSION_VERSION = "CW_Jesse.BetterNetworking.CompressionVersion";

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

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

		public static void InitCompressor()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			byte[] array;
			using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(ZSTD_DICT_RESOURCE_NAME))
			{
				array = new byte[stream.Length];
				stream.Read(array, 0, (int)stream.Length);
			}
			compressor = new Compressor(ZSTD_LEVEL);
			compressor.LoadDictionary(array);
			decompressor = new Decompressor();
			decompressor.LoadDictionary(array);
		}

		public static void InitConfig(ConfigFile config)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			BetterNetworking.configCompressionEnabled = config.Bind<Options_NetworkCompression>("Networking", "Compression Enabled", Options_NetworkCompression.@true, new ConfigDescription("Keep this enabled unless comparing difference.\n---\nCrossplay enabled: Increases speed and strength of network compression.\nCrossplay disabled: Adds network compression.", (AcceptableValueBase)null, Array.Empty<object>()));
			BetterNetworking.configCompressionEnabled.SettingChanged += ConfigCompressionEnabled_SettingChanged;
		}

		private static void ConfigCompressionEnabled_SettingChanged(object sender, EventArgs e)
		{
			SetCompressionEnabledFromConfig();
		}

		private static void SetCompressionEnabledFromConfig()
		{
			bool compressionEnabled;
			if (BetterNetworking.configCompressionEnabled.Value == Options_NetworkCompression.@true)
			{
				compressionEnabled = true;
				BN_Logger.LogMessage("Compression: Enabling");
			}
			else
			{
				compressionEnabled = false;
				BN_Logger.LogMessage("Compression: Disabling");
			}
			CompressionStatus.ourStatus.compressionEnabled = compressionEnabled;
			SendCompressionEnabledStatus();
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		[HarmonyPostfix]
		private static void OnConnect(ref ZNetPeer peer)
		{
			CompressionStatus.AddSocket(peer.m_socket);
			RegisterRPCs(peer);
			SendCompressionVersion(peer);
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		[HarmonyPrefix]
		private static void OnDisconnectPrefix(ZNetPeer peer)
		{
			BN_Logger.LogMessage("Compression: " + BN_Utils.GetPeerName(peer) + " disconnected");
		}

		[HarmonyPatch(typeof(ZNet), "Disconnect")]
		[HarmonyPostfix]
		private static void OnDisconnectPostfix(ZNetPeer peer)
		{
			CompressionStatus.RemoveSocket(peer.m_socket);
		}

		internal static byte[] Compress(byte[] buffer)
		{
			byte[] array = compressor.Wrap((ReadOnlySpan<byte>)buffer).ToArray();
			if (BetterNetworking.configLogMessages.Value >= BN_Logger.Options_Logger_LogLevel.info && buffer.Length > 256)
			{
				float num = (float)array.Length / (float)buffer.Length * 100f;
				BN_Logger.LogInfo(string.Format("Compression: Sent {0} B compressed to {1}%", buffer.Length, num.ToString("0")));
			}
			return array;
		}

		internal static byte[] Decompress(byte[] compressedBuffer)
		{
			byte[] array = decompressor.Unwrap((ReadOnlySpan<byte>)compressedBuffer, int.MaxValue).ToArray();
			if (BetterNetworking.configLogMessages.Value >= BN_Logger.Options_Logger_LogLevel.info && array.Length > 256)
			{
				float num = (float)compressedBuffer.Length / (float)array.Length * 100f;
				BN_Logger.LogInfo(string.Format("Compression: Received {0} B compressed to {1}%", array.Length, num.ToString("0")));
			}
			return array;
		}

		private static void RegisterRPCs(ZNetPeer peer)
		{
			peer.m_rpc.Register<int>("CW_Jesse.BetterNetworking.CompressionVersion", (Action<ZRpc, int>)RPC_CompressionVersion);
			peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressionEnabled", (Action<ZRpc, bool>)RPC_CompressionEnabled);
			peer.m_rpc.Register<bool>("CW_Jesse.BetterNetworking.CompressedStarted", (Action<ZRpc, bool>)RPC_CompressionStarted);
		}

		public static void SendCompressionVersion(ZNetPeer peer)
		{
			peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionVersion", new object[1] { CompressionStatus.ourStatus.version });
		}

		private static void RPC_CompressionVersion(ZRpc rpc, int version)
		{
			ZNetPeer peer = BN_Utils.GetPeer(rpc);
			CompressionStatus.SetVersion(peer.m_socket, version);
			if (CompressionStatus.ourStatus.version == version)
			{
				BN_Logger.LogMessage("Compression: Compatible with " + BN_Utils.GetPeerName(peer));
			}
			else if (CompressionStatus.ourStatus.version > version)
			{
				BN_Logger.LogWarning($"Compression: {BN_Utils.GetPeerName(peer)} ({version}) has an older version of Better Networking; they should update");
			}
			else if (version > 0)
			{
				BN_Logger.LogError($"Compression: {BN_Utils.GetPeerName(peer)} ({version}) has a newer version of Better Networking; you should update");
			}
			if (CompressionStatus.GetIsCompatibleWith(peer.m_socket))
			{
				SendCompressionEnabledStatus(peer);
			}
		}

		private static void SendCompressionEnabledStatus()
		{
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (CompressionStatus.GetIsCompatibleWith(peer.m_socket))
				{
					SendCompressionEnabledStatus(peer);
				}
			}
		}

		private static void SendCompressionEnabledStatus(ZNetPeer peer)
		{
			if (!((Object)(object)ZNet.instance == (Object)null))
			{
				peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressionEnabled", new object[1] { CompressionStatus.ourStatus.compressionEnabled });
				if (CompressionStatus.ourStatus.compressionEnabled && CompressionStatus.GetCompressionEnabled(peer.m_socket))
				{
					SendCompressionStarted(peer, started: true);
				}
				else
				{
					SendCompressionStarted(peer, started: false);
				}
			}
		}

		private static void RPC_CompressionEnabled(ZRpc rpc, bool peerCompressionEnabled)
		{
			ZNetPeer peer = BN_Utils.GetPeer(rpc);
			CompressionStatus.SetCompressionEnabled(peer.m_socket, peerCompressionEnabled);
			if (CompressionStatus.ourStatus.compressionEnabled && peerCompressionEnabled)
			{
				SendCompressionStarted(peer, started: true);
			}
			else
			{
				SendCompressionStarted(peer, started: false);
			}
		}

		private static void SendCompressionStarted(ZNetPeer peer, bool started)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && CompressionStatus.GetSendCompressionStarted(peer.m_socket) != started)
			{
				peer.m_rpc.Invoke("CW_Jesse.BetterNetworking.CompressedStarted", new object[1] { started });
				Flush(peer);
				CompressionStatus.SetSendCompressionStarted(peer.m_socket, started);
				BN_Logger.LogMessage($"Compression: Compression to {BN_Utils.GetPeerName(peer)}: {started}");
			}
		}

		private static void Flush(ZNetPeer peer)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			OnlineBackendType onlineBackend = ZNet.m_onlineBackend;
			if ((int)onlineBackend != 0)
			{
				if ((int)onlineBackend == 1)
				{
					BN_Patch_Compression_PlayFab.FlushQueue(peer.m_socket);
				}
			}
			else
			{
				peer.m_socket.Flush();
			}
		}

		private static void RPC_CompressionStarted(ZRpc rpc, bool peerCompressionStarted)
		{
			ZNetPeer peer = BN_Utils.GetPeer(rpc);
			CompressionStatus.SetReceiveCompressionStarted(peer.m_socket, peerCompressionStarted);
			BN_Logger.LogMessage($"Compression: Compression from {BN_Utils.GetPeerName(peer)}: {peerCompressionStarted}");
		}
	}
	[HarmonyPatch]
	public static class BN_Patch_Compression_PlayFab
	{
		private static Dictionary<PlayFabZLibWorkQueue, ZPlayFabSocket> workQueueSockets = new Dictionary<PlayFabZLibWorkQueue, ZPlayFabSocket>();

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void SocketOpen0(ref ZPlayFabSocket __instance, ref PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			SocketOpen(ref __instance, ref ___m_zlibWorkQueue);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void SocketOpen1(ref ZPlayFabSocket __instance, ref PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			SocketOpen(ref __instance, ref ___m_zlibWorkQueue);
		}

		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPostfix]
		private static void SocketOpen2(ref ZPlayFabSocket __instance, ref PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			SocketOpen(ref __instance, ref ___m_zlibWorkQueue);
		}

		private static void SocketOpen(ref ZPlayFabSocket __instance, ref PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			workQueueSockets.Add(___m_zlibWorkQueue, __instance);
			BN_Logger.LogMessage("PlayFab: Added socket " + BN_Utils.GetPeerName((ISocket)(object)__instance));
		}

		[HarmonyPatch(typeof(ZPlayFabSocket), "Dispose")]
		[HarmonyPostfix]
		private static void SocketClose(ref PlayFabZLibWorkQueue ___m_zlibWorkQueue)
		{
			workQueueSockets.Remove(___m_zlibWorkQueue);
			BN_Logger.LogMessage("PlayFab: Removed socket");
		}

		[HarmonyPatch(typeof(PlayFabZLibWorkQueue), "DoCompress")]
		[HarmonyPrefix]
		private static bool PlayFab_Compress(ref PlayFabZLibWorkQueue __instance, ref Queue<byte[]> ___m_inCompress, ref Queue<byte[]> ___m_outCompress)
		{
			if (!workQueueSockets.TryGetValue(__instance, out var value))
			{
				return true;
			}
			if (!CompressionStatus.GetSendCompressionStarted((ISocket)(object)value))
			{
				return true;
			}
			while (___m_inCompress.Count > 0)
			{
				try
				{
					___m_outCompress.Enqueue(BN_Patch_Compression.Compress(___m_inCompress.Dequeue()));
				}
				catch
				{
					BN_Logger.LogError("PlayFab: Failed BN compress");
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(PlayFabZLibWorkQueue), "DoUncompress")]
		[HarmonyPrefix]
		private static bool PlayFab_Decompress(ref PlayFabZLibWorkQueue __instance, ref Queue<byte[]> ___m_inDecompress, ref Queue<byte[]> ___m_outDecompress)
		{
			bool flag = false;
			if (workQueueSockets.TryGetValue(__instance, out var value))
			{
				flag = CompressionStatus.GetReceiveCompressionStarted((ISocket)(object)value);
			}
			while (___m_inDecompress.Count > 0)
			{
				byte[] array = ___m_inDecompress.Dequeue();
				try
				{
					___m_outDecompress.Enqueue(BN_Patch_Compression.Decompress(array));
					if (!CompressionStatus.GetReceiveCompressionStarted((ISocket)(object)value))
					{
						BN_Logger.LogMessage("PlayFab: Received unexpected compressed message from " + BN_Utils.GetPeerName((ISocket)(object)value));
						CompressionStatus.SetReceiveCompressionStarted((ISocket)(object)value, started: true);
					}
				}
				catch
				{
					if (flag)
					{
						BN_Logger.LogInfo("PlayFab: Failed BN decompress");
					}
					try
					{
						___m_outDecompress.Enqueue((byte[])AccessTools.Method(typeof(PlayFabZLibWorkQueue), "UncompressOnThisThread", (Type[])null, (Type[])null).Invoke(__instance, new object[1] { array }));
						if (CompressionStatus.GetReceiveCompressionStarted((ISocket)(object)value))
						{
							BN_Logger.LogMessage("PlayFab: Received unexpected vanilla message from " + BN_Utils.GetPeerName((ISocket)(object)value));
							CompressionStatus.SetReceiveCompressionStarted((ISocket)(object)value, started: false);
						}
					}
					catch
					{
						BN_Logger.LogMessage("PlayFab: Failed vanilla decompress; keeping data (this data would have been lost without Better Networking)");
						___m_outDecompress.Enqueue(array);
					}
				}
			}
			return false;
		}

		public static void FlushQueue(ISocket socket)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			PlayFabZLibWorkQueue obj = (PlayFabZLibWorkQueue)AccessTools.Field(typeof(ZPlayFabSocket), "m_zlibWorkQueue").GetValue(socket);
			Mutex mutex = (Mutex)AccessTools.Field(typeof(PlayFabZLibWorkQueue), "s_workersMutex").GetValue(obj);
			List<PlayFabZLibWorkQueue> obj2 = (List<PlayFabZLibWorkQueue>)AccessTools.Field(typeof(PlayFabZLibWorkQueue), "s_workers").GetValue(obj);
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayFabZLibWorkQueue), "Execute", (Type[])null, (Type[])null);
			mutex.WaitOne();
			foreach (PlayFabZLibWorkQueue item in obj2)
			{
				methodInfo.Invoke(item, new object[0]);
			}
			mutex.ReleaseMutex();
			AccessTools.Method(typeof(ZPlayFabSocket), "LateUpdate", (Type[])null, (Type[])null).Invoke(socket, null);
		}
	}
	[HarmonyPatch]
	public static class BN_Patch_Compression_Steamworks
	{
		private const int k_nSteamNetworkingSend_Reliable = 8;

		private const int k_cbMaxSteamNetworkingSocketsMessageSizeSend = 524288;

		[HarmonyPatch(typeof(ZSteamSocket), "SendQueuedPackages")]
		[HarmonyPrefix]
		private static bool Steamworks_SendCompressedPackages(ref ZSteamSocket __instance, ref Queue<byte[]> ___m_sendQueue)
		{
			if (!__instance.IsConnected())
			{
				return false;
			}
			if (!CompressionStatus.GetSendCompressionStarted((ISocket)(object)__instance))
			{
				return true;
			}
			___m_sendQueue = new Queue<byte[]>(___m_sendQueue.Select((byte[] p) => BN_Patch_Compression.Compress(p)));
			return true;
		}

		[HarmonyPatch(typeof(ZSteamSocket), "Recv")]
		[HarmonyPostfix]
		private static void Steamworks_ReceiveCompressedPackages(ref ZPackage __result, ref ZSteamSocket __instance)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			if (!__instance.IsConnected() || __result == null)
			{
				return;
			}
			try
			{
				byte[] array = BN_Patch_Compression.Decompress(__result.GetArray());
				__result = new ZPackage(array);
				if (!CompressionStatus.GetReceiveCompressionStarted((ISocket)(object)__instance))
				{
					BN_Logger.LogMessage("Compression (Steamworks): Received unexpected compressed message from " + BN_Utils.GetPeerName((ISocket)(object)__instance));
					CompressionStatus.SetReceiveCompressionStarted((ISocket)(object)__instance, started: true);
				}
			}
			catch
			{
				if (CompressionStatus.GetReceiveCompressionStarted((ISocket)(object)__instance))
				{
					BN_Logger.LogMessage("Compression (Steamworks): Received unexpected uncompressed message from " + BN_Utils.GetPeerName((ISocket)(object)__instance));
					CompressionStatus.SetReceiveCompressionStarted((ISocket)(object)__instance, started: false);
				}
			}
		}
	}
	internal static class CompressionStatus
	{
		public class SocketCompressionStatus
		{
			public int version;

			public bool compressionEnabled;

			public bool receivingCompressed;

			public bool sendingCompressed;
		}

		private const int COMPRESSION_VERSION = 6;

		private const int COMPRESSION_VERSION_UNKNOWN = 0;

		public static SocketCompressionStatus ourStatus = new SocketCompressionStatus
		{
			version = 6,
			compressionEnabled = (BetterNetworking.configCompressionEnabled.Value == BN_Patch_Compression.Options_NetworkCompression.@true)
		};

		private static readonly Dictionary<ISocket, SocketCompressionStatus> socketStatuses = new Dictionary<ISocket, SocketCompressionStatus>();

		public static bool AddSocket(ISocket socket)
		{
			if (socket == null)
			{
				BN_Logger.LogWarning("Compression: Tried to add null peer");
				return false;
			}
			if (IsSocketExist(socket))
			{
				BN_Logger.LogWarning("Compression: Removing existing peer (" + BN_Utils.GetPeerName(socket) + "); did they lose internet or Alt+F4?");
				RemoveSocket(socket);
			}
			BN_Logger.LogMessage("Compression: " + BN_Utils.GetPeerName(socket) + " connected");
			socketStatuses.Add(socket, new SocketCompressionStatus());
			return true;
		}

		public static void RemoveSocket(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				BN_Logger.LogWarning("Compression: Tried to remove non-existent peer: " + BN_Utils.GetPeerName(socket));
			}
			else
			{
				socketStatuses.Remove(socket);
			}
		}

		public static bool IsSocketExist(ISocket socket)
		{
			if (socket != null && socketStatuses.ContainsKey(socket))
			{
				return true;
			}
			return false;
		}

		public static int GetVersion(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				return 0;
			}
			return socketStatuses[socket].version;
		}

		public static void SetVersion(ISocket socket, int theirVersion)
		{
			if (IsSocketExist(socket))
			{
				socketStatuses[socket].version = theirVersion;
			}
		}

		public static bool GetIsCompatibleWith(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				return false;
			}
			return ourStatus.version == GetVersion(socket);
		}

		public static bool GetCompressionEnabled(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				return false;
			}
			return socketStatuses[socket].compressionEnabled;
		}

		public static void SetCompressionEnabled(ISocket socket, bool enabled)
		{
			if (IsSocketExist(socket))
			{
				socketStatuses[socket].compressionEnabled = enabled;
			}
		}

		public static bool GetSendCompressionStarted(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				return false;
			}
			return socketStatuses[socket].sendingCompressed;
		}

		public static void SetSendCompressionStarted(ISocket socket, bool started)
		{
			if (IsSocketExist(socket))
			{
				socketStatuses[socket].sendingCompressed = started;
			}
		}

		public static bool GetReceiveCompressionStarted(ISocket socket)
		{
			if (!IsSocketExist(socket))
			{
				return false;
			}
			return socketStatuses[socket].receivingCompressed;
		}

		public static void SetReceiveCompressionStarted(ISocket socket, bool started)
		{
			if (IsSocketExist(socket))
			{
				socketStatuses[socket].receivingCompressed = started;
			}
		}
	}
}
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) != 0)
				{
					assembly = Assembly.Load(assemblyName);
				}
			}
			return assembly;
		}

		static AssemblyLoader()
		{
			assemblyNames.Add("costura", "costura.costura.dll.compressed");
			symbolNames.Add("costura", "costura.costura.pdb.compressed");
			assemblyNames.Add("microsoft.bcl.asyncinterfaces", "costura.microsoft.bcl.asyncinterfaces.dll.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 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) != 0)
					{
						assembly = Assembly.Load(assemblyName);
					}
				}
				return assembly;
			};
		}
	}
}
internal class CW_JesseBetterNetworking_ProcessedByFody
{
	internal const string FodyVersion = "6.6.4.0";

	internal const string Costura = "5.7.0";
}