Decompiled source of Erenshor COOP v1.0.5

ErenshorCoop/ErenshorCoop.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Timers;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using ErenshorCoop.Client;
using ErenshorCoop.Server;
using ErenshorCoop.Shared;
using ErenshorCoop.Shared.Packets;
using ErenshorCoop.UI;
using HarmonyLib;
using LiteNetLib;
using LiteNetLib.Layers;
using LiteNetLib.Utils;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ErenshorCoop")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ErenshorCoop")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("060ca869-6710-4c26-a2d8-63572e58569b")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ErenshorCoop
{
	public class CommandHandler
	{
		public static void CreateHooks(Harmony harm)
		{
			ErenshorCoopMod.CreatePrefixHook(typeof(TypeText), "CheckCommands", typeof(CommandHandler), "CheckCommands_Prefix");
		}

		public static bool CheckCommands_Prefix(TypeText __instance)
		{
			string text = __instance.typed.text;
			if (string.IsNullOrEmpty(text))
			{
				return true;
			}
			if (text.StartsWith("/"))
			{
				string[] array = text.Substring(1).Split(new char[1] { ' ' });
				string text2 = array[0].ToLower();
				int result = 0;
				switch (text2)
				{
				case "host":
					if (array.Length < 2)
					{
						Logging.LogError("Not enough arguments for \"host\". Usage: /host port");
						return false;
					}
					if (!int.TryParse(array[1], out result))
					{
						Logging.LogError("Could not parse port. (" + array[2] + ")");
					}
					else if (ServerConnectionManager.Instance.StartHost(result))
					{
						ClientConnectionManager.Instance.Connect("localhost", result);
					}
					return false;
				case "connect":
				{
					if (array.Length == 1)
					{
						ClientConnectionManager.Instance.Connect("localhost", 1234);
						return false;
					}
					if (array.Length < 3)
					{
						Logging.LogError("Not enough arguments for \"connect\". Usage: /connect ip port");
						return false;
					}
					string text3 = array[1];
					if (text3 != "localhost" && !IPAddress.TryParse(text3, out IPAddress _))
					{
						Logging.LogError(text3 + " is not a valid IP Address.");
						return false;
					}
					if (!int.TryParse(array[2], out result))
					{
						Logging.LogError("Could not parse port. (" + array[2] + ")");
					}
					else
					{
						ClientConnectionManager.Instance.Connect(text3, result);
					}
					return false;
				}
				case "disconnect":
					ClientConnectionManager.Instance?.Disconnect();
					ServerConnectionManager.Instance?.Disconnect();
					return false;
				}
			}
			return true;
		}
	}
	[BepInPlugin("mizuki.coop", "Erenshor Coop", "1.0.5")]
	public class ErenshorCoopMod : BaseUnityPlugin
	{
		public struct PluginData
		{
			public string name;

			public Version version;
		}

		public static ConfigEntry<int> someConfig;

		public static float mobSyncDistance = 40f;

		public static Action<Scene> OnGameMapLoad;

		public static Action<Scene> OnGameMenuLoad;

		public static SceneChange sceneChange;

		public static ManualLogSource logger;

		private static Harmony harm;

		public static Main ModMain;

		public static List<PluginData> loadedPlugins = new List<PluginData>();

		public static Version version;

		public void Awake()
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			version = ((BaseUnityPlugin)this).Info.Metadata.Version;
			logger = ((BaseUnityPlugin)this).Logger;
			SceneManager.sceneLoaded += OnSceneLoaded;
			OnGameMapLoad = (Action<Scene>)Delegate.Combine(OnGameMapLoad, new Action<Scene>(OnGameLoad));
			OnGameMenuLoad = (Action<Scene>)Delegate.Combine(OnGameMenuLoad, new Action<Scene>(OnMenuLoad));
			GameObject val = new GameObject("Erenshor Coop");
			val.transform.SetParent(((Component)this).transform);
			ClientConfig.Load(((BaseUnityPlugin)this).Config);
			ServerConfig.Load(((BaseUnityPlugin)this).Config);
			val.AddComponent<ClientConnectionManager>();
			val.AddComponent<ServerConnectionManager>();
			val.AddComponent<ClientNPCSyncManager>();
			val.AddComponent<SharedNPCSyncManager>();
			ModMain = new GameObject("CoopUI").AddComponent<Main>();
			((Component)ModMain).transform.SetParent(val.transform);
			EnableHooks();
			GetLoadedPlugins();
			SceneManager.GetActiveScene();
			if ((Object)(object)sceneChange == (Object)null)
			{
				sceneChange = Object.FindObjectOfType<SceneChange>();
			}
			if ((Object)(object)sceneChange == (Object)null)
			{
				return;
			}
			if ((Object)(object)sceneChange.Player != (Object)null && (Object)(object)ClientConnectionManager.Instance.LocalPlayer != (Object)null)
			{
				PlayerSync component = ((Component)sceneChange.Player).GetComponent<PlayerSync>();
				if ((Object)(object)component != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)component);
				}
			}
			else if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer == (Object)null)
			{
				ClientConnectionManager.Instance.LocalPlayer = ((Component)sceneChange.Player).gameObject.AddComponent<PlayerSync>();
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			harm.UnpatchSelf();
		}

		private void GetLoadedPlugins()
		{
			loadedPlugins.Clear();
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				PluginData pluginData = default(PluginData);
				pluginData.name = value.Metadata.Name;
				pluginData.version = value.Metadata.Version;
				PluginData item = pluginData;
				loadedPlugins.Add(item);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded Mods: {item.name} v{item.version}");
			}
		}

		private void OnMenuLoad(Scene scene)
		{
			if (!((Object)(object)ClientConnectionManager.Instance == (Object)null) && !((Object)(object)ClientConnectionManager.Instance.LocalPlayer == (Object)null) && !((Object)(object)sceneChange.Player == (Object)null))
			{
				Object.Destroy((Object)(object)ClientConnectionManager.Instance.LocalPlayer);
				ClientConnectionManager.Instance.LocalPlayer = null;
			}
		}

		private void OnGameLoad(Scene scene)
		{
			if ((Object)(object)sceneChange == (Object)null)
			{
				sceneChange = Object.FindObjectOfType<SceneChange>();
			}
			if (!((Object)(object)sceneChange == (Object)null))
			{
				if ((Object)(object)sceneChange.Player != (Object)null && (Object)(object)ClientConnectionManager.Instance.LocalPlayer == (Object)null)
				{
					ClientConnectionManager.Instance.LocalPlayer = ((Component)sceneChange.Player).gameObject.AddComponent<PlayerSync>();
				}
				else if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer == (Object)null)
				{
					Logging.LogError("Could not find Player object. Try changing scenes?");
				}
			}
		}

		public void Update()
		{
			PacketManager.ExtremePoolNoodleAction();
		}

		public void EnableHooks()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			harm = new Harmony("mizuki.coop");
			GameHooks.CreateHooks(harm);
			CommandHandler.CreateHooks(harm);
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if (((Scene)(ref scene)).name == "LoadScene" || ((Scene)(ref scene)).name == "Menu")
			{
				OnGameMenuLoad?.Invoke(scene);
			}
			else
			{
				OnGameMapLoad?.Invoke(scene);
			}
		}

		public static void CreatePrefixHook(Type origType, string origName, Type newType, string newName)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			try
			{
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(newType, newName, (Type[])null, (Type[])null));
				MethodInfo methodInfo = AccessTools.Method(origType, origName, (Type[])null, (Type[])null);
				harm.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
			}
		}

		public static void CreatePostHook(Type origType, string origName, Type newType, string newName)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			try
			{
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(newType, newName, (Type[])null, (Type[])null));
				MethodInfo methodInfo = AccessTools.Method(origType, origName, (Type[])null, (Type[])null);
				harm.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
			}
		}

		public static void CreatePrefixHook(Type origType, string origName, Type newType, string newName, Type[] paramTypes)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			try
			{
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(newType, newName, (Type[])null, (Type[])null));
				MethodInfo methodInfo = AccessTools.Method(origType, origName, paramTypes, (Type[])null);
				harm.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
			}
		}

		public static void UnPatchTranspiler(Type origType, string origName, Type newType, string newName)
		{
			try
			{
				MethodInfo methodInfo = AccessTools.Method(origType, origName, (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(newType, newName, (Type[])null, (Type[])null);
				harm.Unpatch((MethodBase)methodInfo, methodInfo2);
			}
			catch
			{
			}
		}

		public static void CreateTranspilerHook(Type origType, string origName, Type newType, string newName, Type[] paramTypes)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(origType, origName, paramTypes, (Type[])null);
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(newType, newName, (Type[])null, (Type[])null));
				harm.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
			}
		}

		public static void CreateTranspilerHook(Type origType, string origName, Type newType, string newName)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(origType, origName, (Type[])null, (Type[])null);
				HarmonyMethod val = new HarmonyMethod(AccessTools.Method(newType, newName, (Type[])null, (Type[])null));
				harm.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			catch
			{
			}
		}

		public static Class ClassID2Class(byte _class)
		{
			return (Class)(_class switch
			{
				0 => GameData.ClassDB.Warrior, 
				1 => GameData.ClassDB.Druid, 
				2 => GameData.ClassDB.Duelist, 
				3 => GameData.ClassDB.Arcanist, 
				_ => null, 
			});
		}

		public static byte Class2ClassID(Class _class)
		{
			if ((Object)(object)_class == (Object)(object)GameData.ClassDB.Warrior)
			{
				return 0;
			}
			if ((Object)(object)_class == (Object)(object)GameData.ClassDB.Druid)
			{
				return 1;
			}
			if ((Object)(object)_class == (Object)(object)GameData.ClassDB.Duelist)
			{
				return 2;
			}
			if ((Object)(object)_class == (Object)(object)GameData.ClassDB.Arcanist)
			{
				return 3;
			}
			return byte.MaxValue;
		}
	}
	public class GameHooks
	{
		private static FieldInfo _actualSpawn;

		private static List<GameObject> fightSpawnList = new List<GameObject>();

		private static List<string> fightSpawnListID = new List<string>();

		private static FieldInfo _malarothSpawn;

		private static FieldInfo _chessSpawn;

		private static bool isMalaroth = false;

		private static byte chessSpawn = 0;

		private static bool hasWardOne = false;

		private static bool hasWardTwo = false;

		private static bool hasWardThree = false;

		private static int insertions = 0;

		public static FieldInfo targ;

		public static FieldInfo SpellSource;

		public static FieldInfo xpBonus;

		public static FieldInfo lastTyped;

		public static FieldInfo spawnPoint;

		public static MethodInfo startMethod;

		public static MethodInfo handleNameTag;

		public static FieldInfo rotTimer;

		public static FieldInfo leashing;

		private static FieldInfo thisZoning;

		public static FieldInfo hideHair;

		public static FieldInfo hideHead;

		public static MethodInfo GetTransformNames;

		public static FieldInfo animatorController;

		public static void CreateHooks(Harmony harm)
		{
			ErenshorCoopMod.CreatePrefixHook(typeof(Animator), "SetBool", typeof(GameHooks), "SetBool_Prefix", new Type[2]
			{
				typeof(string),
				typeof(bool)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Animator), "SetFloat", typeof(GameHooks), "SetFloat_Prefix", new Type[2]
			{
				typeof(string),
				typeof(float)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Animator), "SetInteger", typeof(GameHooks), "SetInteger_Prefix", new Type[2]
			{
				typeof(string),
				typeof(int)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Animator), "SetTrigger", typeof(GameHooks), "SetTrigger_Prefix", new Type[1] { typeof(string) });
			ErenshorCoopMod.CreatePrefixHook(typeof(Animator), "ResetTrigger", typeof(GameHooks), "ResetTrigger_Prefix", new Type[1] { typeof(string) });
			ErenshorCoopMod.CreatePrefixHook(typeof(AnimatorOverrideController), "set_Item", typeof(GameHooks), "AnimOverrideSet_Prefix", new Type[2]
			{
				typeof(string),
				typeof(AnimationClip)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Zoneline), "OnTriggerEnter", typeof(GameHooks), "ZoneLineOnTriggerEnter_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(NPC), "Update", typeof(GameHooks), "NPCUpdate_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(NPC), "Combat", typeof(GameHooks), "NPCCombat_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(TypeText), "CheckInput", typeof(GameHooks), "CheckInput_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "ReduceHP", typeof(GameHooks), "StatsReduceHP_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "MitigatePhysical", typeof(GameHooks), "StatsMitigatePhysical_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Character), "DamageMe", typeof(GameHooks), "CharacterDamageMe_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Character), "MagicDamageMe", typeof(GameHooks), "MagicDamageMe_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Character), "CheckVsMR", typeof(GameHooks), "CheckVsMR_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerGrouping), "InviteToGroup", typeof(GameHooks), "InviteToGroup_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerGrouping), "DismissMember1", typeof(GameHooks), "DismissMember1_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerGrouping), "DismissMember2", typeof(GameHooks), "DismissMember2_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerGrouping), "DismissMember3", typeof(GameHooks), "DismissMember3_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerTracking), "SpawnMeInGame", typeof(GameHooks), "SpawnMeInGame_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SpellVessel), "CreateSpellChargeEffect", typeof(GameHooks), "ChargeEffect_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SpellVessel), "EndSpell", typeof(GameHooks), "EndSpell_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SpellVessel), "EndSpellNoCD", typeof(GameHooks), "EndSpell_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SpellVessel), "ResolveSpell", typeof(GameHooks), "ResolveSpell_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerMngr), "BringPlayerGroupToZone", typeof(GameHooks), "BringGroup_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(GameData), "AddExperience", typeof(GameHooks), "AddExperience_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "AddStatusEffect", typeof(GameHooks), "AddStatusEffectType1_Prefix", new Type[3]
			{
				typeof(Spell),
				typeof(bool),
				typeof(int)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "AddStatusEffect", typeof(GameHooks), "AddStatusEffectType2_Prefix", new Type[4]
			{
				typeof(Spell),
				typeof(bool),
				typeof(int),
				typeof(Character)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "AddStatusEffect", typeof(GameHooks), "AddStatusEffectType3_Prefix", new Type[5]
			{
				typeof(Spell),
				typeof(bool),
				typeof(int),
				typeof(Character),
				typeof(float)
			});
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "RemoveStatusEffect", typeof(GameHooks), "RemoveStatusEffect_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "RemoveAllStatusEffects", typeof(GameHooks), "RemoveAllStatusEffects_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Stats), "RemoveBreakableEffects", typeof(GameHooks), "RemoveBreakableEffects_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(MalarothFeed), "CheckForGamepiece", typeof(GameHooks), "MCheckForGamepiece_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(MalarothFeed), "SpawnPiece", typeof(GameHooks), "MSpawnPiece_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Chessboard), "CheckForGamepiece", typeof(GameHooks), "CCheckForGamepiece_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(Chessboard), "SpawnPiece", typeof(GameHooks), "CSpawnPiece_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SiraetheEvent), "Update", typeof(GameHooks), "SUpdate_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(NPCFightEvent), "FixedUpdate", typeof(GameHooks), "FightFixedUpdate_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(CharmedNPC), "GoAway", typeof(GameHooks), "CharmedGoAway_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SimPlayerMngr), "CollectActiveSimData", typeof(GameHooks), "CollectActiveSimData_Prefix");
			ErenshorCoopMod.CreatePrefixHook(typeof(SceneChange), "ChangeScene", typeof(GameHooks), "ChangeScene_Prefix");
			ErenshorCoopMod.CreatePostHook(typeof(SpawnPoint), "SpawnNPC", typeof(GameHooks), "SpawnPointSpawnNPC_Post");
			ErenshorCoopMod.CreatePostHook(typeof(Character), "DamageMe", typeof(GameHooks), "CharacterDamageMe_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(Character), "MagicDamageMe", typeof(GameHooks), "MagicDamageMe_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(Respawn), "RespawnPlayer", typeof(GameHooks), "RespawnPlayer_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(SimPlayerGrouping), "InviteToGroup", typeof(GameHooks), "InviteToGroup_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(GameManager), "OpenEscMenu", typeof(GameHooks), "OpenEscMenu_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(GameManager), "CloseEscMenu", typeof(GameHooks), "CloseEscMenu_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(GameManager), "ToggleEscapeMenu", typeof(GameHooks), "ToggleEscMenu_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(MalarothFeed), "SpawnPiece", typeof(GameHooks), "MSpawnPiece_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(Chessboard), "SpawnPiece", typeof(GameHooks), "CSpawnPiece_Postfix");
			ErenshorCoopMod.CreatePostHook(typeof(SiraetheEvent), "Update", typeof(GameHooks), "SUpdate_Postfix");
			try
			{
				ErenshorCoopMod.UnPatchTranspiler(typeof(SpellVessel), "ResolveSpell", typeof(GameHooks), "ResolveSpellTranspiler");
			}
			catch
			{
			}
			ErenshorCoopMod.CreateTranspilerHook(typeof(SpellVessel), "ResolveSpell", typeof(GameHooks), "ResolveSpellTranspiler");
			if (insertions < 10)
			{
				Logging.LogError("There was an issue resolving healing\nPlease Update to the newest mod version or wait\nuntil a new version has been released.\nHealing might not work, continue at your own risk.");
			}
			Type typeFromHandle = typeof(ModularParts);
			hideHair = typeFromHandle.GetField("hideHair", BindingFlags.Instance | BindingFlags.NonPublic);
			hideHead = typeFromHandle.GetField("hideHead", BindingFlags.Instance | BindingFlags.NonPublic);
			GetTransformNames = typeFromHandle.GetMethod("GetTransformNames", BindingFlags.Instance | BindingFlags.NonPublic);
			lastTyped = typeof(TypeText).GetField("lastTyped", BindingFlags.Instance | BindingFlags.NonPublic);
			Type typeFromHandle2 = typeof(NPC);
			animatorController = typeFromHandle2.GetField("AnimOverride", BindingFlags.Instance | BindingFlags.NonPublic);
			spawnPoint = typeFromHandle2.GetField("MySpawnPoint", BindingFlags.Instance | BindingFlags.NonPublic);
			rotTimer = typeFromHandle2.GetField("rotTimer", BindingFlags.Instance | BindingFlags.NonPublic);
			startMethod = typeFromHandle2.GetMethod("Start", BindingFlags.Instance | BindingFlags.NonPublic);
			handleNameTag = typeFromHandle2.GetMethod("HandleNameTag", BindingFlags.Instance | BindingFlags.NonPublic);
			leashing = typeFromHandle2.GetField("spawnCD", BindingFlags.Instance | BindingFlags.NonPublic);
			Type typeFromHandle3 = typeof(SpellVessel);
			targ = typeFromHandle3.GetField("targ", BindingFlags.Instance | BindingFlags.NonPublic);
			SpellSource = typeFromHandle3.GetField("SpellSource", BindingFlags.Instance | BindingFlags.NonPublic);
			thisZoning = typeof(Zoneline).GetField("thisZoning", BindingFlags.Instance | BindingFlags.NonPublic);
			xpBonus = typeof(Stats).GetField("XPBonus", BindingFlags.Instance | BindingFlags.NonPublic);
			_malarothSpawn = typeof(MalarothFeed).GetField("Spawned", BindingFlags.Instance | BindingFlags.NonPublic);
			_chessSpawn = typeof(Chessboard).GetField("Spawned", BindingFlags.Instance | BindingFlags.NonPublic);
			_actualSpawn = typeof(NPCFightEvent).GetField("actualSpawn", BindingFlags.Instance | BindingFlags.NonPublic);
		}

		public static bool CollectActiveSimData_Prefix(SimPlayerMngr __instance)
		{
			return true;
		}

		public static bool ChangeScene_Prefix(SceneChange __instance, string _dest, Vector3 _landing, bool _useSun, float yRot)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return true;
			}
			if (ClientConnectionManager.Instance.LocalPlayer.currentScene == _dest)
			{
				GameData.SimPlayerGrouping.GroupTargets.Clear();
				GameData.SimPlayerGrouping.isPulling = false;
				GameData.AttackingPlayer.Clear();
				GameData.InCombat = false;
				GameData.GroupMatesInCombat.Clear();
				((Component)GameData.PlayerControl).GetComponent<Fishing>().resetFishing();
				GameData.PlayerControl.HuntingMe.Clear();
				((Collider)((Component)__instance.Player).GetComponent<CharacterController>()).enabled = false;
				GameData.CharmedNPC = null;
				__instance.SetLandingPos(_landing);
				((Component)__instance.Player).transform.position = _landing;
				((Collider)((Component)__instance.Player).GetComponent<CharacterController>()).enabled = true;
				((Behaviour)((Component)__instance.Player).GetComponent<PlayerControl>()).enabled = true;
				GameData.usingSun = _useSun;
				((Component)__instance.Player).transform.eulerAngles = new Vector3(0f, yRot, 0f);
				InZoneEvents.ClearNodes();
				GameData.Zoning = false;
				return false;
			}
			return true;
		}

		public static NetworkedPlayer CreatePlayer(int playerID, Vector3 pos, Quaternion rot)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			List<GameObject> actualSims = GameData.SimMngr.ActualSims;
			if (actualSims.Count == 0)
			{
				Logging.LogError($"Could not create player for {playerID}. No Sim instances.");
				return null;
			}
			GameObject val = actualSims[0];
			if ((Object)(object)val == (Object)null)
			{
				Logging.LogError($"Could not create player for {playerID}. No Sim.");
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(val.gameObject, pos, rot);
			val2.SetActive(false);
			SimPlayer component = val2.GetComponent<SimPlayer>();
			if ((Object)(object)component == (Object)null)
			{
				Logging.LogError($"Could not create player for {playerID}.");
				return null;
			}
			((Behaviour)component).enabled = false;
			NavMeshAgent component2 = val2.GetComponent<NavMeshAgent>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Behaviour)component2).enabled = false;
			}
			NetworkedPlayer networkedPlayer = val2.AddComponent<NetworkedPlayer>();
			networkedPlayer.sim = component;
			networkedPlayer.pos = pos;
			networkedPlayer.rot = rot;
			return networkedPlayer;
		}

		public static void CharmedGoAway_Prefix(CharmedNPC __instance)
		{
			if (ClientConnectionManager.Instance.IsRunning && GameData.PlayerControl.Myself.MyCharmedNPC.SummonedByPlayer)
			{
				ClientConnectionManager.Instance.LocalPlayer.DespawnSummon();
			}
		}

		public static bool FightFixedUpdate_Prefix(NPCFightEvent __instance)
		{
			//IL_0298: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: 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)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0303: Unknown result type (might be due to invalid IL or missing references)
			//IL_030e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return true;
			}
			if (!ClientZoneOwnership.isZoneOwner)
			{
				return true;
			}
			bool flag = false;
			if ((Object)(object)__instance.MyNPC.CurrentAggroTarget != (Object)null && __instance.MyStats.Myself.Alive)
			{
				if (__instance.PercentagesToSpawn.Count > 0 && (float)__instance.MyStats.CurrentHP / (float)__instance.MyStats.CurrentMaxHP * 100f < __instance.PercentagesToSpawn[0])
				{
					UpdateSocialLog.LogAdd(__instance.PromptOnSpawn, "orange");
					__instance.PercentagesToSpawn.RemoveAt(0);
					int num = 0;
					foreach (GameObject spawnAdd in __instance.SpawnAdds)
					{
						GameObject item = Object.Instantiate<GameObject>(spawnAdd, ((Component)__instance).transform.position + new Vector3((float)Random.Range(-11, 11), 0f, (float)Random.Range(-11, 11)), ((Component)__instance).transform.rotation);
						fightSpawnList.Add(item);
						fightSpawnListID.Add($"1,{num}");
						num++;
					}
					flag = true;
				}
				if (__instance.SpawnAddsEveryXSeconds > 0f)
				{
					float num2 = (float)_actualSpawn.GetValue(__instance);
					if (num2 > 0f)
					{
						_actualSpawn.SetValue(__instance, num2 - 1f);
					}
					else
					{
						UpdateSocialLog.LogAdd(__instance.PromptOnSpawn, "orange");
						int num3 = 0;
						foreach (GameObject spawnAdd2 in __instance.SpawnAdds)
						{
							GameObject item2 = Object.Instantiate<GameObject>(spawnAdd2, ((Component)__instance).transform.position + new Vector3((float)Random.Range(-11, 11), 0f, (float)Random.Range(-11, 11)), ((Component)__instance).transform.rotation);
							fightSpawnList.Add(item2);
							fightSpawnListID.Add($"2,{num3}");
							num3++;
						}
						_actualSpawn.SetValue(__instance, __instance.SpawnAddsEveryXSeconds * 60f);
						flag = true;
					}
				}
			}
			if (__instance.SpawnOnDeath.Count > 0 && !__instance.MyStats.Myself.Alive)
			{
				int num4 = 0;
				foreach (GameObject item4 in __instance.SpawnOnDeath)
				{
					Vector3 position = ((Component)__instance).transform.position;
					Vector3 localScale = ((Component)__instance).transform.localScale;
					float num5 = 0f - ((Vector3)(ref localScale)).magnitude;
					localScale = ((Component)__instance).transform.localScale;
					float num6 = Random.Range(num5, ((Vector3)(ref localScale)).magnitude);
					localScale = ((Component)__instance).transform.localScale;
					float num7 = 0f - ((Vector3)(ref localScale)).magnitude;
					localScale = ((Component)__instance).transform.localScale;
					GameObject item3 = Object.Instantiate<GameObject>(item4, position + new Vector3(num6, 0f, Random.Range(num7, ((Vector3)(ref localScale)).magnitude)), ((Component)__instance).transform.rotation);
					fightSpawnList.Add(item3);
					fightSpawnListID.Add($"3,{num4}");
					num4++;
				}
				__instance.SpawnOnDeath.Clear();
				UpdateSocialLog.LogAdd(__instance.PromptOnSpawn, "orange");
				if (__instance.InstantDespawn)
				{
					__instance.MyStats.Myself.MyNPC.ExpediteRot();
				}
				flag = true;
			}
			if (fightSpawnList.Count > 0)
			{
				SharedNPCSyncManager.Instance.ServerSpawnMobs(fightSpawnList, fightSpawnListID, -4, __instance.MyNPC);
				fightSpawnList.Clear();
				fightSpawnListID.Clear();
			}
			return !flag;
		}

		public static bool MCheckForGamepiece_Prefix()
		{
			if (ClientConnectionManager.Instance.IsRunning && !ClientZoneOwnership.isZoneOwner)
			{
				return false;
			}
			return true;
		}

		public static bool CCheckForGamepiece_Prefix()
		{
			if (ClientConnectionManager.Instance.IsRunning && !ClientZoneOwnership.isZoneOwner)
			{
				return false;
			}
			return true;
		}

		public static bool SUpdate_Prefix()
		{
			if (ClientConnectionManager.Instance.IsRunning && !ClientZoneOwnership.isZoneOwner)
			{
				hasWardOne = false;
				hasWardTwo = false;
				hasWardThree = false;
				return false;
			}
			return true;
		}

		public static void MSpawnPiece_Prefix(MalarothFeed __instance, GameObject _npc)
		{
			if (ClientConnectionManager.Instance.IsRunning)
			{
				isMalaroth = (Object)(object)_npc == (Object)(object)__instance.Malaroth;
			}
		}

		public static void CSpawnPiece_Prefix(Chessboard __instance, GameObject _npc)
		{
			if (ClientConnectionManager.Instance.IsRunning)
			{
				if ((Object)(object)_npc == (Object)(object)__instance.PeonNPC)
				{
					chessSpawn = 1;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.EmberNPC)
				{
					chessSpawn = 2;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.BlazeNPC)
				{
					chessSpawn = 3;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.MonarchNPC)
				{
					chessSpawn = 4;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.KingsmanNPC)
				{
					chessSpawn = 5;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.CandlekeeperNPC)
				{
					chessSpawn = 6;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.FacelessDuel)
				{
					chessSpawn = 7;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.FacelessArc)
				{
					chessSpawn = 8;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.FacelessPal)
				{
					chessSpawn = 9;
				}
				if ((Object)(object)_npc == (Object)(object)__instance.FacelessDru)
				{
					chessSpawn = 10;
				}
			}
		}

		public static void MSpawnPiece_Postfix(MalarothFeed __instance, GameObject _npc)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if (ClientConnectionManager.Instance.IsRunning)
			{
				object? value = _malarothSpawn.GetValue(__instance);
				Character val = (Character)((value is Character) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					SharedNPCSyncManager.Instance.ServerSpawnMob(((Component)val).gameObject, -1, 0, isMalaroth, ((Component)val).transform.position, ((Component)val).transform.rotation);
				}
			}
		}

		public static void CSpawnPiece_Postfix(Chessboard __instance, GameObject _npc)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (ClientConnectionManager.Instance.IsRunning)
			{
				object? value = _chessSpawn.GetValue(__instance);
				Character val = (Character)((value is Character) ? value : null);
				if ((Object)(object)val != (Object)null)
				{
					SharedNPCSyncManager.Instance.ServerSpawnMob(((Component)val).gameObject, -2, chessSpawn, isRare: false, ((Component)val).transform.position, ((Component)val).transform.rotation);
				}
			}
		}

		public static void SUpdate_Postfix(SiraetheEvent __instance)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			if (ClientConnectionManager.Instance.IsRunning && ClientZoneOwnership.isZoneOwner)
			{
				if ((Object)(object)__instance.WardOne != (Object)null && !hasWardOne)
				{
					SharedNPCSyncManager.Instance.ServerSpawnMob(__instance.WardOne, -3, 1, isRare: false, __instance.WardOne.transform.position, __instance.WardOne.transform.rotation);
					hasWardOne = true;
				}
				else if ((Object)(object)__instance.WardOne == (Object)null && hasWardOne)
				{
					hasWardOne = false;
				}
				if ((Object)(object)__instance.WardTwo != (Object)null && !hasWardTwo)
				{
					SharedNPCSyncManager.Instance.ServerSpawnMob(__instance.WardTwo, -3, 2, isRare: false, __instance.WardTwo.transform.position, __instance.WardTwo.transform.rotation);
					hasWardTwo = true;
				}
				else if ((Object)(object)__instance.WardTwo == (Object)null && hasWardTwo)
				{
					hasWardTwo = false;
				}
				if ((Object)(object)__instance.WardThree != (Object)null && !hasWardThree)
				{
					SharedNPCSyncManager.Instance.ServerSpawnMob(__instance.WardThree, -3, 3, isRare: false, __instance.WardThree.transform.position, __instance.WardThree.transform.rotation);
					hasWardThree = true;
				}
				else if ((Object)(object)__instance.WardThree == (Object)null && hasWardThree)
				{
					hasWardThree = false;
				}
			}
		}

		public static IEnumerable<CodeInstruction> ResolveSpellTranspiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Expected O, but got Unknown
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Expected O, but got Unknown
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Expected O, but got Unknown
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Expected O, but got Unknown
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0264: Expected O, but got Unknown
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Expected O, but got Unknown
			//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ad: Expected O, but got Unknown
			//IL_0569: Unknown result type (might be due to invalid IL or missing references)
			//IL_0573: Expected O, but got Unknown
			//IL_0594: Unknown result type (might be due to invalid IL or missing references)
			//IL_059e: Expected O, but got Unknown
			//IL_05b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bc: Expected O, but got Unknown
			//IL_05dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_05e7: Expected O, but got Unknown
			//IL_05f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Expected O, but got Unknown
			//IL_0613: Unknown result type (might be due to invalid IL or missing references)
			//IL_061d: Expected O, but got Unknown
			//IL_062b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0635: Expected O, but got Unknown
			//IL_0656: Unknown result type (might be due to invalid IL or missing references)
			//IL_0660: Expected O, but got Unknown
			//IL_066e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0678: Expected O, but got Unknown
			//IL_0686: Unknown result type (might be due to invalid IL or missing references)
			//IL_0690: Expected O, but got Unknown
			//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c1: Expected O, but got Unknown
			List<CodeInstruction> codes2 = new List<CodeInstruction>(instructions);
			Label item = default(Label);
			Label item2 = default(Label);
			for (int j = 0; j < codes2.Count; j++)
			{
				if (codes2[j].opcode == OpCodes.Switch && codes2[j].operand is Label[] array)
				{
					item = array[6];
					item2 = array[7];
				}
				codes2[j].labels.Contains(item);
				codes2[j].labels.Contains(item2);
				bool flag = codes2[j].operand is FieldInfo;
				FieldInfo fieldInfo = codes2[j].operand as FieldInfo;
				if (j - 7 >= 0 && codes2[j].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentHP" && fieldInfo.DeclaringType == typeof(Stats) && codes2[j - 7].opcode == OpCodes.Stloc_S && ((LocalBuilder)codes2[j - 7].operand).LocalIndex == 18)
				{
					int num = j + 1;
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "targ")));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldloc_S, (object)16));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldloc_S, (object)17));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
					codes2.Insert(num++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
					MethodInfo methodInfo = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
					codes2.Insert(num++, new CodeInstruction(OpCodes.Call, (object)methodInfo));
					insertions++;
					j = num - 1;
				}
				if (codes2.Count >= j + 1 && codes2[j].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentHP" && fieldInfo.DeclaringType == typeof(Stats) && codes2[j + 1].opcode == OpCodes.Ldsfld)
				{
					FieldInfo fieldInfo2 = codes2[j + 1].operand as FieldInfo;
					if (fieldInfo2 != null && fieldInfo2.Name == "GroupMember1" && fieldInfo2.DeclaringType == typeof(GameData))
					{
						InsertSyncHealingCalls(codes2, ref j, j + 1, fieldInfo2.Name);
					}
					if (fieldInfo2 != null && fieldInfo2.Name == "GroupMember2" && fieldInfo2.DeclaringType == typeof(GameData))
					{
						InsertSyncHealingCalls(codes2, ref j, j + 1, fieldInfo2.Name);
					}
					if (fieldInfo2 != null && fieldInfo2.Name == "GroupMember3" && fieldInfo2.DeclaringType == typeof(GameData))
					{
						InsertSyncHealingCalls(codes2, ref j, j + 1, fieldInfo2.Name);
					}
				}
				if (codes2.Count >= j + 4 && codes2[j].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentHP" && fieldInfo.DeclaringType == typeof(Stats) && codes2[j + 4].opcode == OpCodes.Ldfld)
				{
					FieldInfo fieldInfo3 = codes2[j + 4].operand as FieldInfo;
					if (fieldInfo3 != null && fieldInfo3.Name == "MyCharmedNPC")
					{
						_ = fieldInfo3.DeclaringType == typeof(Character);
					}
				}
				if (codes2.Count >= j + 1 && codes2[j].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentHP" && fieldInfo.DeclaringType == typeof(Stats) && codes2[j + 1].opcode == OpCodes.Ldloc_S)
				{
					int num2 = j + 1;
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldloc_S, (object)22));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Character), "MyStats")));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldloc_S, (object)17));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
					MethodInfo methodInfo2 = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
					codes2.Insert(num2++, new CodeInstruction(OpCodes.Call, (object)methodInfo2));
					insertions++;
					j = num2 - 1;
				}
				DoMPCalls(ref codes2, ref j);
			}
			return codes2.AsEnumerable();
			static void InsertSyncHealingCalls(List<CodeInstruction> codes, ref int i, int insertAt, string groupMemberName)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Expected O, but got Unknown
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Expected O, but got Unknown
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Expected O, but got Unknown
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Expected O, but got Unknown
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Expected O, but got Unknown
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c5: Expected O, but got Unknown
				//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dc: Expected O, but got Unknown
				//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
				//IL_0106: Expected O, but got Unknown
				//IL_0113: Unknown result type (might be due to invalid IL or missing references)
				//IL_011d: Expected O, but got Unknown
				//IL_012a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Expected O, but got Unknown
				//IL_0158: Unknown result type (might be due to invalid IL or missing references)
				//IL_0162: Expected O, but got Unknown
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldsfld, (object)AccessTools.Field(typeof(GameData), groupMemberName)));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SimPlayerTracking), "MyStats")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldloc_S, (object)17));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
				MethodInfo methodInfo3 = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Call, (object)methodInfo3));
				i = insertAt - 1;
				insertions++;
			}
		}

		private static void DoMPCalls(ref List<CodeInstruction> codes, ref int i)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected O, but got Unknown
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Expected O, but got Unknown
			//IL_024c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Expected O, but got Unknown
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Expected O, but got Unknown
			//IL_0554: Unknown result type (might be due to invalid IL or missing references)
			//IL_055e: Expected O, but got Unknown
			//IL_0580: Unknown result type (might be due to invalid IL or missing references)
			//IL_058a: Expected O, but got Unknown
			//IL_059f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a9: Expected O, but got Unknown
			//IL_05cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d5: Expected O, but got Unknown
			//IL_05e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ee: Expected O, but got Unknown
			//IL_0610: Unknown result type (might be due to invalid IL or missing references)
			//IL_061a: Expected O, but got Unknown
			//IL_063c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0646: Expected O, but got Unknown
			//IL_0659: Unknown result type (might be due to invalid IL or missing references)
			//IL_0663: Expected O, but got Unknown
			//IL_0672: Unknown result type (might be due to invalid IL or missing references)
			//IL_067c: Expected O, but got Unknown
			//IL_069e: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a8: Expected O, but got Unknown
			//IL_06b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c1: Expected O, but got Unknown
			//IL_06d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_06da: Expected O, but got Unknown
			//IL_0702: Unknown result type (might be due to invalid IL or missing references)
			//IL_070c: Expected O, but got Unknown
			bool flag = codes[i].operand is FieldInfo;
			FieldInfo fieldInfo = codes[i].operand as FieldInfo;
			if (codes.Count >= i + 3 && codes[i].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentMana" && fieldInfo.DeclaringType == typeof(Stats))
			{
				FieldInfo fieldInfo2 = codes[i + 3].operand as FieldInfo;
				if (fieldInfo2 != null && fieldInfo2.DeclaringType == typeof(Spell) && fieldInfo2.Name == "GroupEffect")
				{
					int num = i + 1;
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "targ")));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Spell), "Mana")));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldstr, (object)""));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
					codes.Insert(num++, new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
					MethodInfo methodInfo = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
					codes.Insert(num++, new CodeInstruction(OpCodes.Call, (object)methodInfo));
					insertions++;
					i = num - 1;
				}
			}
			if (codes.Count >= i + 1 && codes[i].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentMana" && fieldInfo.DeclaringType == typeof(Stats) && codes[i + 1].opcode == OpCodes.Ldsfld)
			{
				FieldInfo fieldInfo3 = codes[i + 1].operand as FieldInfo;
				if (fieldInfo3 != null && fieldInfo3.Name == "GroupMember1" && fieldInfo3.DeclaringType == typeof(GameData))
				{
					InsertSyncHealingCalls(codes, ref i, i + 1, fieldInfo3.Name);
				}
				if (fieldInfo3 != null && fieldInfo3.Name == "GroupMember2" && fieldInfo3.DeclaringType == typeof(GameData))
				{
					InsertSyncHealingCalls(codes, ref i, i + 1, fieldInfo3.Name);
				}
				if (fieldInfo3 != null && fieldInfo3.Name == "GroupMember3" && fieldInfo3.DeclaringType == typeof(GameData))
				{
					InsertSyncHealingCalls(codes, ref i, i + 1, fieldInfo3.Name);
				}
			}
			if (codes.Count >= i + 4 && codes[i].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentMana" && fieldInfo.DeclaringType == typeof(Stats) && codes[i + 4].opcode == OpCodes.Ldfld)
			{
				FieldInfo fieldInfo4 = codes[i + 4].operand as FieldInfo;
				if (fieldInfo4 != null && fieldInfo4.Name == "MyCharmedNPC")
				{
					_ = fieldInfo4.DeclaringType == typeof(Character);
				}
			}
			if (codes.Count >= i + 1 && codes[i].opcode == OpCodes.Stfld && flag && fieldInfo.Name == "CurrentMana" && fieldInfo.DeclaringType == typeof(Stats) && codes[i + 1].opcode == OpCodes.Ldloc_S)
			{
				int num2 = i + 1;
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldloc_S, (object)23));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Character), "MyStats")));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Spell), "Mana")));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldstr, (object)""));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
				codes.Insert(num2++, new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
				MethodInfo methodInfo2 = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
				codes.Insert(num2++, new CodeInstruction(OpCodes.Call, (object)methodInfo2));
				insertions++;
				i = num2 - 1;
			}
			static void InsertSyncHealingCalls(List<CodeInstruction> codes, ref int i, int insertAt, string groupMemberName)
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Expected O, but got Unknown
				//IL_0037: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Expected O, but got Unknown
				//IL_005d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0067: Expected O, but got Unknown
				//IL_0087: Unknown result type (might be due to invalid IL or missing references)
				//IL_0091: Expected O, but got Unknown
				//IL_009e: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a8: Expected O, but got Unknown
				//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d2: Expected O, but got Unknown
				//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00fc: Expected O, but got Unknown
				//IL_010d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0117: Expected O, but got Unknown
				//IL_0124: Unknown result type (might be due to invalid IL or missing references)
				//IL_012e: Expected O, but got Unknown
				//IL_014e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0158: Expected O, but got Unknown
				//IL_0165: Unknown result type (might be due to invalid IL or missing references)
				//IL_016f: Expected O, but got Unknown
				//IL_017c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0186: Expected O, but got Unknown
				//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
				//IL_01b4: Expected O, but got Unknown
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldsfld, (object)AccessTools.Field(typeof(GameData), groupMemberName)));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SimPlayerTracking), "MyStats")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "spell")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(Spell), "Mana")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldstr, (object)""));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldfld, (object)AccessTools.Field(typeof(SpellVessel), "SpellSource")));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Ldc_I4_1, (object)null));
				MethodInfo methodInfo3 = AccessTools.Method(typeof(GameHooks), "SyncHealing", (Type[])null, (Type[])null);
				codes.Insert(insertAt++, new CodeInstruction(OpCodes.Call, (object)methodInfo3));
				i = insertAt - 1;
				insertions++;
			}
		}

		public static void SyncHealing(Spell spell, Stats target, int amount, string text, CastSpell caster, bool calc, bool isMP)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			var (flag, flag2, num) = GetEntityIDByCharacter(caster.MyChar);
			if (num == -1)
			{
				Logging.Log("no caster? " + ((Object)caster).name);
				return;
			}
			if (!flag && ((flag2 && !ServerConnectionManager.Instance.IsRunning) || (!flag2 && !ClientZoneOwnership.isZoneOwner)))
			{
				Logging.Log($"er {flag} {flag2} {num}");
				return;
			}
			if (flag && num != ClientConnectionManager.Instance.LocalPlayerID)
			{
				Logging.Log("er NoLocalPlayer");
				return;
			}
			if (amount == 0 && !isMP)
			{
				amount = spell.HP + caster.MyChar.MyStats.CharacterClass.WisBenefit / 100 * caster.MyChar.MyStats.GetCurrentWis();
			}
			var (flag3, flag4, num2) = GetEntityIDByCharacter(target.Myself);
			if (num2 == -1)
			{
				Logging.Log($"no target {flag3} {flag4} {num2} {((Object)target).name}");
				return;
			}
			bool isCrit = !isMP && text.Contains("CRITICAL");
			HealingData healingData = default(HealingData);
			healingData.amount = amount;
			healingData.isCrit = isCrit;
			healingData.isMP = isMP;
			healingData.targetID = num2;
			healingData.targetIsNPC = !flag3 && !flag4;
			healingData.targetIsSim = flag4;
			HealingData hd = healingData;
			if (!flag)
			{
				((NPCSync)SharedNPCSyncManager.Instance.GetEntityFromID(num, flag2)).SendHeal(hd);
			}
			else
			{
				ClientConnectionManager.Instance.LocalPlayer.SendHeal(hd);
			}
		}

		public static void OpenEscMenu_Postfix()
		{
			Main.isGameMenuOpen = true;
			if ((Object)(object)Main.connectUI != (Object)null)
			{
				Main.connectUI.SetActive(true);
			}
		}

		public static void CloseEscMenu_Postfix()
		{
			Main.isGameMenuOpen = false;
			if ((Object)(object)Main.connectUI != (Object)null)
			{
				Main.connectUI.SetActive(false);
			}
		}

		public static void ToggleEscMenu_Postfix(GameManager __instance)
		{
			Main.isGameMenuOpen = __instance.EscapeMenu.activeSelf;
			if ((Object)(object)Main.connectUI != (Object)null)
			{
				Main.connectUI.SetActive(Main.isGameMenuOpen);
			}
		}

		public static bool AddExperience_Prefix(int xp, bool useMod)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return true;
			}
			if (Grouping.currentGroup.groupList == null || Grouping.currentGroup.groupList.Count <= 1)
			{
				return true;
			}
			if (Grouping.currentGroup.groupList.Count > 1)
			{
				if (Grouping.IsLocalLeader())
				{
					float num = (float)xpBonus.GetValue(GameData.PlayerStats);
					if (!ServerConnectionManager.Instance.IsRunning)
					{
						PacketManager.GetOrCreatePacket<GroupPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.GROUP).AddPacketData(GroupDataType.EXPERIENCE, "xp", xp).SetData("xpBonus", num)
							.SetData("useMod", useMod);
					}
					else
					{
						Grouping.ServerHandleXP(ClientConnectionManager.Instance.LocalPlayerID, xp, useMod, num);
					}
					return false;
				}
				return false;
			}
			return true;
		}

		public static bool BringGroup_Prefix(SimPlayerMngr __instance)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return true;
			}
			if (!ServerConnectionManager.Instance.IsRunning)
			{
				return false;
			}
			Scene activeScene;
			if (GameData.GroupMember1 != null && GameData.GroupMember1.simIndex >= 0)
			{
				__instance.ActiveSimInstances.Add(GameData.GroupMember1.SpawnMeInGame(((Component)GameData.PlayerControl).transform.position + new Vector3((float)Random.Range(-1, 1), 0f, (float)Random.Range(-1, 1))));
				GameData.GroupMember1.MyAvatar.InGroup = true;
				GameData.GroupMember1.isPuller = false;
				GameData.GroupMember1.Caution = false;
				SimPlayerTracking groupMember = GameData.GroupMember1;
				activeScene = SceneManager.GetActiveScene();
				groupMember.CurScene = ((Scene)(ref activeScene)).name;
				List<string> simsInZones = __instance.SimsInZones;
				int simIndex = GameData.GroupMember1.simIndex;
				activeScene = SceneManager.GetActiveScene();
				simsInZones[simIndex] = ((Scene)(ref activeScene)).name;
			}
			if (GameData.GroupMember2 != null && GameData.GroupMember2.simIndex >= 0)
			{
				__instance.ActiveSimInstances.Add(GameData.GroupMember2.SpawnMeInGame(((Component)GameData.PlayerControl).transform.position + new Vector3((float)Random.Range(-1, 1), 0f, (float)Random.Range(-1, 1))));
				GameData.GroupMember2.MyAvatar.InGroup = true;
				GameData.GroupMember2.isPuller = false;
				GameData.GroupMember2.Caution = false;
				SimPlayerTracking groupMember2 = GameData.GroupMember2;
				activeScene = SceneManager.GetActiveScene();
				groupMember2.CurScene = ((Scene)(ref activeScene)).name;
				List<string> simsInZones2 = __instance.SimsInZones;
				int simIndex2 = GameData.GroupMember2.simIndex;
				activeScene = SceneManager.GetActiveScene();
				simsInZones2[simIndex2] = ((Scene)(ref activeScene)).name;
			}
			if (GameData.GroupMember3 != null && GameData.GroupMember3.simIndex >= 0)
			{
				__instance.ActiveSimInstances.Add(GameData.GroupMember3.SpawnMeInGame(((Component)GameData.PlayerControl).transform.position + new Vector3((float)Random.Range(-1, 1), 0f, (float)Random.Range(-1, 1))));
				GameData.GroupMember3.MyAvatar.InGroup = true;
				GameData.GroupMember3.isPuller = false;
				GameData.GroupMember3.Caution = false;
				SimPlayerTracking groupMember3 = GameData.GroupMember3;
				activeScene = SceneManager.GetActiveScene();
				groupMember3.CurScene = ((Scene)(ref activeScene)).name;
				List<string> simsInZones3 = __instance.SimsInZones;
				int simIndex3 = GameData.GroupMember3.simIndex;
				activeScene = SceneManager.GetActiveScene();
				simsInZones3[simIndex3] = ((Scene)(ref activeScene)).name;
			}
			return false;
		}

		public static void ResolveSpell_Prefix(SpellVessel __instance)
		{
			//IL_001c: 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_012d: Expected O, but got Unknown
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			bool flag = true;
			short num = -1;
			Character myChar = ((CastSpell)SpellSource.GetValue(__instance)).MyChar;
			EntityType entityType = EntityType.ENEMY;
			if ((Object)(object)myChar != (Object)(object)ClientConnectionManager.Instance.LocalPlayer.character)
			{
				if (!ServerConnectionManager.Instance.IsRunning)
				{
					return;
				}
				foreach (KeyValuePair<short, NPCSync> mob in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = mob.Key;
						entityType = mob.Value.type;
						break;
					}
				}
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = sim.Key;
						entityType = sim.Value.type;
						break;
					}
				}
				if (flag)
				{
					return;
				}
			}
			Stats val = (Stats)targ.GetValue(__instance);
			bool targetIsNPC = false;
			short num2 = -1;
			bool targetIsSim = false;
			if (!ClientZoneOwnership.isZoneOwner)
			{
				foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
				{
					if ((Object)(object)networkedMob.Value.character.MyStats == (Object)(object)val)
					{
						targetIsNPC = true;
						num2 = networkedMob.Key;
						break;
					}
				}
			}
			else
			{
				foreach (KeyValuePair<short, NPCSync> mob2 in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob2.Value.character.MyStats == (Object)(object)val)
					{
						targetIsNPC = true;
						num2 = mob2.Key;
						break;
					}
				}
				foreach (KeyValuePair<short, NPCSync> sim2 in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim2.Value.character.MyStats == (Object)(object)val)
					{
						targetIsNPC = true;
						num2 = sim2.Key;
						targetIsSim = true;
						break;
					}
				}
			}
			if (num2 == -1)
			{
				foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
				{
					if ((Object)(object)player.Value.sim.MyStats == (Object)(object)val)
					{
						num2 = player.Key;
						break;
					}
				}
				if (num2 == -1 && (Object)(object)val == (Object)(object)ClientConnectionManager.Instance.LocalPlayer.stats)
				{
					num2 = ClientConnectionManager.Instance.LocalPlayerID;
				}
			}
			if (num2 == -1)
			{
				Logging.Log("no valid target? " + ((Object)val).name);
			}
			else if (!flag)
			{
				EntityActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<EntityActionPacket>(num, PacketType.ENTITY_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.SPELL_EFFECT);
				orCreatePacket.spellID = ((BaseScriptableObject)__instance.spell).Id;
				orCreatePacket.targetID = num2;
				orCreatePacket.targetIsNPC = targetIsNPC;
				orCreatePacket.targetIsSim = targetIsSim;
				orCreatePacket.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
				if (ServerConnectionManager.Instance.IsRunning && SharedNPCSyncManager.Instance.sims.ContainsKey(num))
				{
					entityType = EntityType.SIM;
				}
				orCreatePacket.entityType = entityType;
			}
			else
			{
				PlayerActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket2.dataTypes.Add(ActionType.SPELL_EFFECT);
				orCreatePacket2.spellID = ((BaseScriptableObject)__instance.spell).Id;
				orCreatePacket2.targetID = num2;
				orCreatePacket2.targetIsNPC = targetIsNPC;
				orCreatePacket2.targetIsSim = targetIsSim;
			}
		}

		public static void EndSpell_Prefix(SpellVessel __instance)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Invalid comparison between Unknown and I4
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Invalid comparison between Unknown and I4
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			bool flag = true;
			short num = -1;
			Character myChar = ((CastSpell)SpellSource.GetValue(__instance)).MyChar;
			EntityType entityType = EntityType.ENEMY;
			if ((Object)(object)myChar != (Object)(object)ClientConnectionManager.Instance.LocalPlayer.character)
			{
				if (!ClientZoneOwnership.isZoneOwner)
				{
					return;
				}
				foreach (KeyValuePair<short, NPCSync> mob in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = mob.Key;
						entityType = mob.Value.type;
						break;
					}
				}
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = sim.Key;
						entityType = sim.Value.type;
						break;
					}
				}
				if (flag)
				{
					return;
				}
			}
			if (flag)
			{
				PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION).dataTypes.Add(ActionType.SPELL_END);
				if ((int)__instance.spell.Type == 7 && (Object)(object)myChar.MyCharmedNPC != (Object)null)
				{
					ClientConnectionManager.Instance.LocalPlayer.CreateSummon(__instance.spell, ((Component)myChar.MyCharmedNPC).gameObject);
				}
				return;
			}
			EntityActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<EntityActionPacket>(num, PacketType.ENTITY_ACTION);
			orCreatePacket.dataTypes.Add(ActionType.SPELL_END);
			orCreatePacket.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
			if (ServerConnectionManager.Instance.IsRunning && SharedNPCSyncManager.Instance.sims.ContainsKey(num))
			{
				entityType = EntityType.SIM;
			}
			if ((int)__instance.spell.Type == 7 && (Object)(object)myChar.MyCharmedNPC != (Object)null)
			{
				GetEntityByCharacter(myChar).CreateSummon(__instance.spell, ((Component)myChar.MyCharmedNPC).gameObject);
			}
			orCreatePacket.entityType = entityType;
		}

		public static void ChargeEffect_Prefix(SpellVessel __instance, Spell _spell, Transform _caster, Stats _target, CastSpell _source, float _castTime, bool _useMana, bool _resonate)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			bool flag = true;
			short num = -1;
			Character myChar = _source.MyChar;
			EntityType entityType = EntityType.ENEMY;
			if ((Object)(object)myChar != (Object)(object)ClientConnectionManager.Instance.LocalPlayer.character)
			{
				if (!ClientZoneOwnership.isZoneOwner)
				{
					return;
				}
				foreach (KeyValuePair<short, NPCSync> mob in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = mob.Key;
						entityType = mob.Value.type;
						break;
					}
				}
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character == (Object)(object)myChar)
					{
						flag = false;
						num = sim.Key;
						entityType = sim.Value.type;
						break;
					}
				}
				if (flag)
				{
					return;
				}
			}
			if (flag)
			{
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.SPELL_CHARGE);
				orCreatePacket.SpellChargeFXIndex = _spell.SpellChargeFXIndex;
				return;
			}
			EntityActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<EntityActionPacket>(num, PacketType.ENTITY_ACTION);
			orCreatePacket2.dataTypes.Add(ActionType.SPELL_CHARGE);
			orCreatePacket2.SpellChargeFXIndex = _spell.SpellChargeFXIndex;
			orCreatePacket2.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
			if (ServerConnectionManager.Instance.IsRunning && SharedNPCSyncManager.Instance.sims.ContainsKey(num))
			{
				entityType = EntityType.SIM;
			}
			orCreatePacket2.entityType = entityType;
		}

		public static bool SpawnMeInGame_Prefix(SimPlayerTracking __instance)
		{
			if (__instance.simIndex < 0)
			{
				return false;
			}
			return true;
		}

		public static bool DismissMember1_Prefix(SimPlayerGrouping __instance)
		{
			if (ClientConnectionManager.Instance.IsRunning && GameData.GroupMember1 != null && (Object)(object)GameData.GroupMember1.MyAvatar != (Object)null && GameData.GroupMember1.simIndex < 0)
			{
				int num = Math.Abs(GameData.GroupMember1.simIndex) - 1;
				Entity playerFromID = ClientConnectionManager.Instance.GetPlayerFromID((short)num);
				if ((Object)(object)playerFromID != (Object)null && (Object)(object)((NetworkedPlayer)playerFromID).sim.MyStats == (Object)(object)GameData.GroupMember1.MyStats)
				{
					Grouping.RemoveFromGroup((short)num);
					return false;
				}
			}
			if (ServerConnectionManager.Instance.IsRunning && GameData.GroupMember1 != null && (Object)(object)GameData.GroupMember1.MyAvatar != (Object)null && GameData.GroupMember1.simIndex >= 0)
			{
				NPCSync component = ((Component)GameData.GroupMember1.MyAvatar).GetComponent<NPCSync>();
				if ((Object)(object)component != (Object)null)
				{
					SharedNPCSyncManager.Instance.ServerRemoveSim(component.entityID);
					Grouping.RemoveSim(GameData.GroupMember1);
				}
			}
			return true;
		}

		public static bool DismissMember2_Prefix(SimPlayerGrouping __instance)
		{
			if (ClientConnectionManager.Instance.IsRunning && GameData.GroupMember2 != null && (Object)(object)GameData.GroupMember2.MyAvatar != (Object)null && GameData.GroupMember2.simIndex < 0)
			{
				int num = Math.Abs(GameData.GroupMember2.simIndex) - 1;
				Entity playerFromID = ClientConnectionManager.Instance.GetPlayerFromID((short)num);
				if ((Object)(object)playerFromID != (Object)null && (Object)(object)((NetworkedPlayer)playerFromID).sim.MyStats == (Object)(object)GameData.GroupMember2.MyStats)
				{
					Grouping.RemoveFromGroup((short)num);
					return false;
				}
			}
			if (ServerConnectionManager.Instance.IsRunning && GameData.GroupMember2 != null && (Object)(object)GameData.GroupMember2.MyAvatar != (Object)null && GameData.GroupMember2.simIndex >= 0)
			{
				NPCSync component = ((Component)GameData.GroupMember2.MyAvatar).GetComponent<NPCSync>();
				if ((Object)(object)component != (Object)null)
				{
					SharedNPCSyncManager.Instance.ServerRemoveSim(component.entityID);
					Grouping.RemoveSim(GameData.GroupMember2);
				}
			}
			return true;
		}

		public static bool DismissMember3_Prefix(SimPlayerGrouping __instance)
		{
			if (ClientConnectionManager.Instance.IsRunning && GameData.GroupMember3 != null && (Object)(object)GameData.GroupMember3.MyAvatar != (Object)null && GameData.GroupMember3.simIndex < 0)
			{
				int num = Math.Abs(GameData.GroupMember3.simIndex) - 1;
				Entity playerFromID = ClientConnectionManager.Instance.GetPlayerFromID((short)num);
				if ((Object)(object)playerFromID != (Object)null && (Object)(object)((NetworkedPlayer)playerFromID).sim.MyStats == (Object)(object)GameData.GroupMember3.MyStats)
				{
					Grouping.RemoveFromGroup((short)num);
					return false;
				}
			}
			if (ServerConnectionManager.Instance.IsRunning && GameData.GroupMember3 != null && (Object)(object)GameData.GroupMember3.MyAvatar != (Object)null && GameData.GroupMember3.simIndex >= 0)
			{
				NPCSync component = ((Component)GameData.GroupMember3.MyAvatar).GetComponent<NPCSync>();
				if ((Object)(object)component != (Object)null)
				{
					SharedNPCSyncManager.Instance.ServerRemoveSim(component.entityID);
					Grouping.RemoveSim(GameData.GroupMember3);
				}
			}
			return true;
		}

		public static void InviteToGroup_Postfix(SimPlayerGrouping __instance)
		{
			if (ServerConnectionManager.Instance.IsRunning)
			{
				if (GameData.GroupMember1 != null && GameData.GroupMember1.simIndex >= 0)
				{
					Grouping.InviteSim(GameData.GroupMember1);
				}
				if (GameData.GroupMember2 != null && GameData.GroupMember2.simIndex >= 0)
				{
					Grouping.InviteSim(GameData.GroupMember2);
				}
				if (GameData.GroupMember3 != null && GameData.GroupMember3.simIndex >= 0)
				{
					Grouping.InviteSim(GameData.GroupMember3);
				}
			}
		}

		public static bool InviteToGroup_Prefix(SimPlayerGrouping __instance)
		{
			if (ClientConnectionManager.Instance.IsRunning)
			{
				Character currentTarget = GameData.PlayerControl.CurrentTarget;
				NetworkedPlayer networkedPlayer = null;
				if ((Object)(object)currentTarget != (Object)null && (Object)(object)(networkedPlayer = ((Component)currentTarget).GetComponent<NetworkedPlayer>()) != (Object)null)
				{
					if (!GameData.PlayerControl.Myself.Alive)
					{
						return true;
					}
					Grouping.InvitePlayer(networkedPlayer);
					return false;
				}
				if ((Object)(object)currentTarget != (Object)null && !ServerConnectionManager.Instance.IsRunning)
				{
					Logging.LogGameMessage("[Grouping] Only the host can invite sims.");
					return false;
				}
			}
			return true;
		}

		public static void RespawnPlayer_Postfix()
		{
			if (ClientConnectionManager.Instance.IsRunning)
			{
				PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION).dataTypes.Add(ActionType.REVIVE);
			}
		}

		public static bool MiscGenPopup_Prefix(int _dmg, bool _crit, DamageType _type, Transform _tar)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (ClientConnectionManager.Instance.IsRunning)
			{
				Debug.Log((object)$"{_dmg} {_crit} {_type} {_tar.position}");
			}
			return true;
		}

		private static (bool, bool, short) GetEntityIDByCharacter(Character _char)
		{
			foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
			{
				if ((Object)(object)networkedMob.Value.character == (Object)(object)_char)
				{
					return (false, false, networkedMob.Value.entityID);
				}
			}
			foreach (KeyValuePair<short, NetworkedNPC> networkedSim in ClientNPCSyncManager.Instance.NetworkedSims)
			{
				if ((Object)(object)networkedSim.Value.character == (Object)(object)_char)
				{
					return (false, true, networkedSim.Value.entityID);
				}
			}
			foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
			{
				if ((Object)(object)player.Value.character == (Object)(object)_char)
				{
					return (true, false, player.Value.entityID);
				}
			}
			if (ServerConnectionManager.Instance.IsRunning)
			{
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character == (Object)(object)_char)
					{
						return (false, true, sim.Value.entityID);
					}
				}
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.character == (Object)(object)_char)
			{
				return (true, false, ClientConnectionManager.Instance.LocalPlayerID);
			}
			return (false, false, -1);
		}

		public static Entity GetEntityByCharacter(Character _char)
		{
			foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
			{
				if ((Object)(object)networkedMob.Value.character == (Object)(object)_char)
				{
					return networkedMob.Value;
				}
			}
			foreach (KeyValuePair<short, NetworkedNPC> networkedSim in ClientNPCSyncManager.Instance.NetworkedSims)
			{
				if ((Object)(object)networkedSim.Value.character == (Object)(object)_char)
				{
					return networkedSim.Value;
				}
			}
			foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
			{
				if ((Object)(object)player.Value.character == (Object)(object)_char)
				{
					return player.Value;
				}
			}
			if (ServerConnectionManager.Instance.IsRunning)
			{
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character == (Object)(object)_char)
					{
						return sim.Value;
					}
				}
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.character == (Object)(object)_char)
			{
				return ClientConnectionManager.Instance.LocalPlayer;
			}
			return null;
		}

		private static (bool, bool, short) GetEntityIDByStats(Stats _char)
		{
			foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
			{
				if ((Object)(object)networkedMob.Value.character.MyStats == (Object)(object)_char)
				{
					return (false, false, networkedMob.Value.entityID);
				}
			}
			foreach (KeyValuePair<short, NetworkedNPC> networkedSim in ClientNPCSyncManager.Instance.NetworkedSims)
			{
				if ((Object)(object)networkedSim.Value.character.MyStats == (Object)(object)_char)
				{
					return (false, true, networkedSim.Value.entityID);
				}
			}
			foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
			{
				if ((Object)(object)player.Value.character.MyStats == (Object)(object)_char)
				{
					return (true, false, player.Value.entityID);
				}
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.character.MyStats == (Object)(object)_char)
			{
				return (true, false, ClientConnectionManager.Instance.LocalPlayerID);
			}
			return (false, false, -1);
		}

		private static Entity GetEntityByStats(Stats _char)
		{
			if (!ClientZoneOwnership.isZoneOwner)
			{
				foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
				{
					if ((Object)(object)networkedMob.Value.character.MyStats == (Object)(object)_char)
					{
						return networkedMob.Value;
					}
				}
				foreach (KeyValuePair<short, NetworkedNPC> networkedSim in ClientNPCSyncManager.Instance.NetworkedSims)
				{
					if ((Object)(object)networkedSim.Value.character.MyStats == (Object)(object)_char)
					{
						return networkedSim.Value;
					}
				}
			}
			else
			{
				foreach (KeyValuePair<short, NPCSync> mob in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob.Value.character.MyStats == (Object)(object)_char)
					{
						return mob.Value;
					}
				}
				foreach (KeyValuePair<short, NPCSync> sim in SharedNPCSyncManager.Instance.sims)
				{
					if ((Object)(object)sim.Value.character.MyStats == (Object)(object)_char)
					{
						return sim.Value;
					}
				}
			}
			foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
			{
				if ((Object)(object)player.Value.character.MyStats == (Object)(object)_char)
				{
					return player.Value;
				}
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.character.MyStats == (Object)(object)_char)
			{
				return ClientConnectionManager.Instance.LocalPlayer;
			}
			return null;
		}

		public static void AddStatusEffectType1_Prefix(Stats __instance, Spell spell, bool _fromPlayer, int _dmgBonus)
		{
			if (ClientConnectionManager.Instance.IsRunning && (Object)(object)ClientConnectionManager.Instance.LocalPlayer.stats == (Object)(object)__instance && !Variables.DontCheckEffectCharacters.Contains(ClientConnectionManager.Instance.LocalPlayer))
			{
				string id = ((BaseScriptableObject)spell).Id;
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket.effectData = new StatusEffectData
				{
					spellID = id,
					damageBonus = _dmgBonus,
					targetID = -1,
					duration = -1f
				};
				if (id == "83280423440")
				{
					Logging.Log("StatusEffectData 1:\n- spellID: " + orCreatePacket.effectData.spellID + "\n" + $"- damageBonus: {orCreatePacket.effectData.damageBonus}\n" + $"- attackerIsPlayer: {orCreatePacket.effectData.casterType}\n" + $"- attackerID: {orCreatePacket.effectData.casterID}\n" + $"- duration: {orCreatePacket.effectData.duration}\n");
				}
			}
		}

		public static void AddStatusEffectType2_Prefix(Stats __instance, Spell spell, bool _fromPlayer, int _dmgBonus, Character _specificCaster)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			Entity entityByStats = GetEntityByStats(__instance);
			Entity entityByCharacter = GetEntityByCharacter(_specificCaster);
			if ((Object)(object)entityByStats == (Object)null || (Object)(object)entityByCharacter == (Object)null || Variables.DontCheckEffectCharacters.Contains(entityByStats))
			{
				return;
			}
			if ((Object)(object)entityByStats == (Object)(object)ClientConnectionManager.Instance.LocalPlayer && (Object)(object)entityByCharacter == (Object)(object)ClientConnectionManager.Instance.LocalPlayer)
			{
				string id = ((BaseScriptableObject)spell).Id;
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket.effectData = new StatusEffectData
				{
					spellID = id,
					damageBonus = _dmgBonus,
					casterType = EntityType.PLAYER,
					targetID = -2,
					duration = -1f
				};
				if (id == "83280423440")
				{
					Logging.Log("StatusEffectData 2:\n- spellID: " + orCreatePacket.effectData.spellID + "\n" + $"- damageBonus: {orCreatePacket.effectData.damageBonus}\n" + $"- attackerIsPlayer: {orCreatePacket.effectData.casterType}\n" + $"- attackerID: {orCreatePacket.effectData.casterID}\n" + $"- duration: {orCreatePacket.effectData.duration}\n");
				}
			}
			else if ((Object)(object)entityByCharacter == (Object)(object)ClientConnectionManager.Instance.LocalPlayer)
			{
				string id2 = ((BaseScriptableObject)spell).Id;
				PlayerActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket2.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket2.effectData = new StatusEffectData
				{
					spellID = id2,
					damageBonus = _dmgBonus,
					casterType = EntityType.PLAYER,
					casterID = entityByCharacter.entityID,
					duration = -1f,
					targetID = entityByStats.entityID,
					targetType = entityByStats.type
				};
			}
			else if (ClientZoneOwnership.isZoneOwner)
			{
				string id3 = ((BaseScriptableObject)spell).Id;
				EntityActionPacket orCreatePacket3 = PacketManager.GetOrCreatePacket<EntityActionPacket>(entityByCharacter.entityID, PacketType.ENTITY_ACTION);
				orCreatePacket3.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket3.effectData = new StatusEffectData
				{
					spellID = id3,
					damageBonus = _dmgBonus,
					casterType = entityByCharacter.type,
					casterID = entityByCharacter.entityID,
					duration = -1f,
					targetID = entityByStats.entityID,
					targetType = entityByStats.type
				};
				orCreatePacket3.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
			}
		}

		public static void AddStatusEffectType3_Prefix(Stats __instance, Spell spell, bool _fromPlayer, int _dmgBonus, Character _specificCaster, float _duration)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			Entity entityByStats = GetEntityByStats(__instance);
			Entity entityByCharacter = GetEntityByCharacter(_specificCaster);
			if ((Object)(object)entityByStats == (Object)null || (Object)(object)entityByCharacter == (Object)null || Variables.DontCheckEffectCharacters.Contains(entityByStats))
			{
				return;
			}
			if ((Object)(object)entityByStats == (Object)(object)ClientConnectionManager.Instance.LocalPlayer && (Object)(object)entityByCharacter == (Object)(object)ClientConnectionManager.Instance.LocalPlayer)
			{
				string id = ((BaseScriptableObject)spell).Id;
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket.effectData = new StatusEffectData
				{
					spellID = id,
					damageBonus = _dmgBonus,
					casterType = EntityType.PLAYER,
					targetID = -3,
					duration = _duration
				};
				if (id == "83280423440")
				{
					Logging.Log("StatusEffectData 3:\n- spellID: " + orCreatePacket.effectData.spellID + "\n" + $"- damageBonus: {orCreatePacket.effectData.damageBonus}\n" + $"- attackerIsPlayer: {orCreatePacket.effectData.casterType}\n" + $"- attackerID: {orCreatePacket.effectData.casterID}\n" + $"- duration: {orCreatePacket.effectData.duration}\n");
				}
			}
			else if ((Object)(object)entityByCharacter == (Object)(object)ClientConnectionManager.Instance.LocalPlayer)
			{
				string id2 = ((BaseScriptableObject)spell).Id;
				PlayerActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket2.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket2.effectData = new StatusEffectData
				{
					spellID = id2,
					damageBonus = _dmgBonus,
					casterType = EntityType.PLAYER,
					casterID = entityByCharacter.entityID,
					duration = _duration,
					targetID = entityByStats.entityID,
					targetType = entityByStats.type
				};
			}
			else if (ClientZoneOwnership.isZoneOwner)
			{
				string id3 = ((BaseScriptableObject)spell).Id;
				EntityActionPacket orCreatePacket3 = PacketManager.GetOrCreatePacket<EntityActionPacket>(entityByCharacter.entityID, PacketType.ENTITY_ACTION);
				orCreatePacket3.dataTypes.Add(ActionType.STATUS_EFFECT_APPLY);
				orCreatePacket3.effectData = new StatusEffectData
				{
					spellID = id3,
					damageBonus = _dmgBonus,
					casterType = entityByCharacter.type,
					casterID = entityByCharacter.entityID,
					duration = _duration,
					targetID = entityByStats.entityID,
					targetType = entityByStats.type
				};
				orCreatePacket3.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
			}
		}

		public static void RemoveStatusEffect_Prefix(Stats __instance, int index)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.stats == (Object)(object)__instance)
			{
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
				orCreatePacket.statusID = index;
			}
			else if (ClientZoneOwnership.isZoneOwner)
			{
				Entity entityByStats = GetEntityByStats(__instance);
				if (!((Object)(object)entityByStats == (Object)null))
				{
					EntityActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<EntityActionPacket>(entityByStats.entityID, PacketType.ENTITY_ACTION);
					orCreatePacket2.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
					orCreatePacket2.statusID = index;
					orCreatePacket2.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
				}
			}
		}

		public static void RemoveAllStatusEffects_Prefix(Stats __instance)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.stats == (Object)(object)__instance)
			{
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
				orCreatePacket.RemoveAllStatus = true;
			}
			else if (ClientZoneOwnership.isZoneOwner)
			{
				Entity entityByStats = GetEntityByStats(__instance);
				if (!((Object)(object)entityByStats == (Object)null))
				{
					EntityActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<EntityActionPacket>(entityByStats.entityID, PacketType.ENTITY_ACTION);
					orCreatePacket2.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
					orCreatePacket2.RemoveAllStatus = true;
					orCreatePacket2.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
				}
			}
		}

		public static void RemoveBreakableEffects_Prefix(Stats __instance)
		{
			if (!ClientConnectionManager.Instance.IsRunning)
			{
				return;
			}
			if ((Object)(object)ClientConnectionManager.Instance.LocalPlayer.stats == (Object)(object)__instance)
			{
				PlayerActionPacket orCreatePacket = PacketManager.GetOrCreatePacket<PlayerActionPacket>(ClientConnectionManager.Instance.LocalPlayerID, PacketType.PLAYER_ACTION);
				orCreatePacket.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
				orCreatePacket.RemoveBreakable = true;
			}
			else if (ClientZoneOwnership.isZoneOwner)
			{
				Entity entityByStats = GetEntityByStats(__instance);
				if (!((Object)(object)entityByStats == (Object)null))
				{
					EntityActionPacket orCreatePacket2 = PacketManager.GetOrCreatePacket<EntityActionPacket>(entityByStats.entityID, PacketType.ENTITY_ACTION);
					orCreatePacket2.dataTypes.Add(ActionType.STATUS_EFFECT_REMOVE);
					orCreatePacket2.RemoveBreakable = true;
					orCreatePacket2.targetPlayerIDs = SharedNPCSyncManager.Instance.GetPlayerSendList();
				}
			}
		}

		public static bool StatsMitigatePhysical_Prefix(Stats __instance, ref int __result, int _incomingDmg)
		{
			Character component = ((Component)__instance).gameObject.GetComponent<Character>();
			if ((Object)(object)component == (Object)null)
			{
				return true;
			}
			if (Variables.DontCalculateDamageMitigationCharacters.Contains(component))
			{
				__result = _incomingDmg;
				return false;
			}
			return true;
		}

		public static bool StatsReduceHP_Prefix(Stats __instance, ref bool __result, int _dmg, DamageType _dmgType)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			if (!ClientZoneOwnership.isZoneOwner && ClientConnectionManager.Instance.IsRunning)
			{
				foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
				{
					if ((Object)(object)networkedMob.Value.character.MyStats == (Object)(object)__instance)
					{
						GameData.Misc.GenPopup(_dmg, false, _dmgType, ((Component)__instance).transform);
						__result = __instance.CurrentHP <= 0;
						return false;
					}
				}
				foreach (KeyValuePair<short, NetworkedNPC> networkedSim in ClientNPCSyncManager.Instance.NetworkedSims)
				{
					if ((Object)(object)networkedSim.Value.character.MyStats == (Object)(object)__instance)
					{
						GameData.Misc.GenPopup(_dmg, false, _dmgType, ((Component)__instance).transform);
						__result = __instance.CurrentHP <= 0;
						return false;
					}
				}
				foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
				{
					if ((Object)(object)player.Value.character.MyStats == (Object)(object)__instance)
					{
						GameData.Misc.GenPopup(_dmg, false, _dmgType, ((Component)__instance).transform);
						__result = __instance.CurrentHP <= 0;
						return false;
					}
				}
			}
			return true;
		}

		public static bool CheckVsMR_Prefix(Character __instance, ref float __result)
		{
			if (Variables.DontCalculateDamageMitigationCharacters.Contains(__instance))
			{
				__result = 0f;
				return false;
			}
			return true;
		}

		private static void SyncDamage(Character attackedChar, int __result, DamageType _dmgType, Character _attacker, bool _animEffect, float resistMod)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			NPCSync component = ((Component)attackedChar).GetComponent<NPCSync>();
			PlayerSync component2 = ((Component)attackedChar).GetComponent<PlayerSync>();
			NetworkedPlayer component3 = ((Component)attackedChar).GetComponent<NetworkedPlayer>();
			NetworkedNPC component4 = ((Component)attackedChar).GetComponent<NetworkedNPC>();
			if ((Object)(object)_attacker == (Object)null)
			{
				Logging.LogError("no attacker");
				return;
			}
			NPCSync component5 = ((Component)_attacker).GetComponent<NPCSync>();
			bool flag = (Object)(object)component != (Object)null;
			bool flag2 = (Object)(object)component2 != (Object)null;
			bool flag3 = (Object)(object)component3 != (Object)null;
			bool flag4 = (Object)(object)component4 != (Object)null;
			if ((Object)(object)component5 != (Object)null && (flag || flag2 || flag3 || flag4))
			{
				if (flag)
				{
					component5.SendAttack(__result, component.entityID, attackerNPC: true, _dmgType, _animEffect, resistMod);
				}
				else if (flag3)
				{
					component5.SendAttack(__result, component3.playerID, attackerNPC: false, _dmgType, _animEffect, resistMod);
				}
				else if (flag2)
				{
					component5.SendAttack(__result, ClientConnectionManager.Instance.LocalPlayerID, attackerNPC: false, _dmgType, _animEffect, resistMod);
				}
				else if (flag4)
				{
					component5.SendAttack(__result, component4.entityID, attackerNPC: true, _dmgType, _animEffect, resistMod);
				}
			}
		}

		private static void SyncDamageClient(Character attackedChar, int damage, DamageType _dmgType, Character _attacker, bool _animEffect, float resistMod)
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_attacker == (Object)null)
			{
				return;
			}
			NetworkedNPC component = ((Component)attackedChar).GetComponent<NetworkedNPC>();
			NetworkedPlayer component2 = ((Component)attackedChar).GetComponent<NetworkedPlayer>();
			NPCSync component3 = ((Component)attackedChar).GetComponent<NPCSync>();
			bool flag = (Object)(object)component != (Object)null;
			bool flag2 = (Object)(object)component2 != (Object)null;
			bool flag3 = (Object)(object)component3 != (Object)null;
			if (!flag && !flag2 && !flag3)
			{
				return;
			}
			short num = -1;
			bool attackedIsNPC = false;
			if (flag)
			{
				foreach (KeyValuePair<short, NetworkedNPC> networkedMob in ClientNPCSyncManager.Instance.NetworkedMobs)
				{
					if ((Object)(object)networkedMob.Value == (Object)(object)component)
					{
						num = networkedMob.Key;
						attackedIsNPC = true;
						break;
					}
				}
			}
			else if (flag2)
			{
				foreach (KeyValuePair<short, NetworkedPlayer> player in ClientConnectionManager.Instance.Players)
				{
					if ((Object)(object)player.Value == (Object)(object)component2)
					{
						num = player.Key;
						break;
					}
				}
			}
			else if (flag3)
			{
				foreach (KeyValuePair<short, NPCSync> mob in SharedNPCSyncManager.Instance.mobs)
				{
					if ((Object)(object)mob.Value == (Object)(object)component3)
					{
						num = mob.Key;
						attackedIsNPC = true;
						break;
					}
				}
			}
			if (num != -1)
			{
				ClientConnectionManager.Instance.LocalPlayer.SendDamageAttack(damage, num, attackedIsNPC, _dmgType, _animEffect, resistMod);
			}
		}

		publ

ErenshorCoop/LiteNetLib.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using LiteNetLib.Layers;
using LiteNetLib.Utils;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")]
[assembly: AssemblyCompany("Ruslan Pyrch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright 2024 Ruslan Pyrch")]
[assembly: AssemblyDescription("Lite reliable UDP library for .NET, Mono, and .NET Core")]
[assembly: AssemblyFileVersion("1.3.1")]
[assembly: AssemblyInformationalVersion("1.0.0+f57b9eafe72b924681a371d671af911478b6ab88")]
[assembly: AssemblyProduct("LiteNetLib")]
[assembly: AssemblyTitle("LiteNetLib")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/RevenantX/LiteNetLib")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.1.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsUnmanagedAttribute : Attribute
	{
	}
}
namespace LiteNetLib
{
	internal abstract class BaseChannel
	{
		protected readonly NetPeer Peer;

		protected readonly Queue<NetPacket> OutgoingQueue = new Queue<NetPacket>(64);

		private int _isAddedToPeerChannelSendQueue;

		public int PacketsInQueue => OutgoingQueue.Count;

		protected BaseChannel(NetPeer peer)
		{
			Peer = peer;
		}

		public void AddToQueue(NetPacket packet)
		{
			lock (OutgoingQueue)
			{
				OutgoingQueue.Enqueue(packet);
			}
			AddToPeerChannelSendQueue();
		}

		protected void AddToPeerChannelSendQueue()
		{
			if (Interlocked.CompareExchange(ref _isAddedToPeerChannelSendQueue, 1, 0) == 0)
			{
				Peer.AddToReliableChannelSendQueue(this);
			}
		}

		public bool SendAndCheckQueue()
		{
			bool num = SendNextPackets();
			if (!num)
			{
				Interlocked.Exchange(ref _isAddedToPeerChannelSendQueue, 0);
			}
			return num;
		}

		protected abstract bool SendNextPackets();

		public abstract bool ProcessPacket(NetPacket packet);
	}
	internal enum ConnectionRequestResult
	{
		None,
		Accept,
		Reject,
		RejectForce
	}
	public class ConnectionRequest
	{
		private readonly NetManager _listener;

		private int _used;

		internal NetConnectRequestPacket InternalPacket;

		public readonly IPEndPoint RemoteEndPoint;

		public NetDataReader Data => InternalPacket.Data;

		internal ConnectionRequestResult Result { get; private set; }

		internal void UpdateRequest(NetConnectRequestPacket connectRequest)
		{
			if (connectRequest.ConnectionTime >= InternalPacket.ConnectionTime && (connectRequest.ConnectionTime != InternalPacket.ConnectionTime || connectRequest.ConnectionNumber != InternalPacket.ConnectionNumber))
			{
				InternalPacket = connectRequest;
			}
		}

		private bool TryActivate()
		{
			return Interlocked.CompareExchange(ref _used, 1, 0) == 0;
		}

		internal ConnectionRequest(IPEndPoint remoteEndPoint, NetConnectRequestPacket requestPacket, NetManager listener)
		{
			InternalPacket = requestPacket;
			RemoteEndPoint = remoteEndPoint;
			_listener = listener;
		}

		public NetPeer AcceptIfKey(string key)
		{
			if (!TryActivate())
			{
				return null;
			}
			try
			{
				if (Data.GetString() == key)
				{
					Result = ConnectionRequestResult.Accept;
				}
			}
			catch
			{
				NetDebug.WriteError("[AC] Invalid incoming data");
			}
			if (Result == ConnectionRequestResult.Accept)
			{
				return _listener.OnConnectionSolved(this, null, 0, 0);
			}
			Result = ConnectionRequestResult.Reject;
			_listener.OnConnectionSolved(this, null, 0, 0);
			return null;
		}

		public NetPeer Accept()
		{
			if (!TryActivate())
			{
				return null;
			}
			Result = ConnectionRequestResult.Accept;
			return _listener.OnConnectionSolved(this, null, 0, 0);
		}

		public void Reject(byte[] rejectData, int start, int length, bool force)
		{
			if (TryActivate())
			{
				Result = (force ? ConnectionRequestResult.RejectForce : ConnectionRequestResult.Reject);
				_listener.OnConnectionSolved(this, rejectData, start, length);
			}
		}

		public void Reject(byte[] rejectData, int start, int length)
		{
			Reject(rejectData, start, length, force: false);
		}

		public void RejectForce(byte[] rejectData, int start, int length)
		{
			Reject(rejectData, start, length, force: true);
		}

		public void RejectForce()
		{
			Reject(null, 0, 0, force: true);
		}

		public void RejectForce(byte[] rejectData)
		{
			Reject(rejectData, 0, rejectData.Length, force: true);
		}

		public void RejectForce(NetDataWriter rejectData)
		{
			Reject(rejectData.Data, 0, rejectData.Length, force: true);
		}

		public void Reject()
		{
			Reject(null, 0, 0, force: false);
		}

		public void Reject(byte[] rejectData)
		{
			Reject(rejectData, 0, rejectData.Length, force: false);
		}

		public void Reject(NetDataWriter rejectData)
		{
			Reject(rejectData.Data, 0, rejectData.Length, force: false);
		}
	}
	public enum UnconnectedMessageType
	{
		BasicMessage,
		Broadcast
	}
	public enum DisconnectReason
	{
		ConnectionFailed,
		Timeout,
		HostUnreachable,
		NetworkUnreachable,
		RemoteConnectionClose,
		DisconnectPeerCalled,
		ConnectionRejected,
		InvalidProtocol,
		UnknownHost,
		Reconnect,
		PeerToPeerConnection,
		PeerNotFound
	}
	public struct DisconnectInfo
	{
		public DisconnectReason Reason;

		public SocketError SocketErrorCode;

		public NetPacketReader AdditionalData;
	}
	public interface INetEventListener
	{
		void OnPeerConnected(NetPeer peer);

		void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);

		void OnNetworkError(IPEndPoint endPoint, SocketError socketError);

		void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod);

		void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);

		void OnNetworkLatencyUpdate(NetPeer peer, int latency);

		void OnConnectionRequest(ConnectionRequest request);
	}
	public interface IDeliveryEventListener
	{
		void OnMessageDelivered(NetPeer peer, object userData);
	}
	public interface INtpEventListener
	{
		void OnNtpResponse(NtpPacket packet);
	}
	public interface IPeerAddressChangedListener
	{
		void OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress);
	}
	public class EventBasedNetListener : INetEventListener, IDeliveryEventListener, INtpEventListener, IPeerAddressChangedListener
	{
		public delegate void OnPeerConnected(NetPeer peer);

		public delegate void OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo);

		public delegate void OnNetworkError(IPEndPoint endPoint, SocketError socketError);

		public delegate void OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channel, DeliveryMethod deliveryMethod);

		public delegate void OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType);

		public delegate void OnNetworkLatencyUpdate(NetPeer peer, int latency);

		public delegate void OnConnectionRequest(ConnectionRequest request);

		public delegate void OnDeliveryEvent(NetPeer peer, object userData);

		public delegate void OnNtpResponseEvent(NtpPacket packet);

		public delegate void OnPeerAddressChangedEvent(NetPeer peer, IPEndPoint previousAddress);

		public event OnPeerConnected PeerConnectedEvent;

		public event OnPeerDisconnected PeerDisconnectedEvent;

		public event OnNetworkError NetworkErrorEvent;

		public event OnNetworkReceive NetworkReceiveEvent;

		public event OnNetworkReceiveUnconnected NetworkReceiveUnconnectedEvent;

		public event OnNetworkLatencyUpdate NetworkLatencyUpdateEvent;

		public event OnConnectionRequest ConnectionRequestEvent;

		public event OnDeliveryEvent DeliveryEvent;

		public event OnNtpResponseEvent NtpResponseEvent;

		public event OnPeerAddressChangedEvent PeerAddressChangedEvent;

		public void ClearPeerConnectedEvent()
		{
			this.PeerConnectedEvent = null;
		}

		public void ClearPeerDisconnectedEvent()
		{
			this.PeerDisconnectedEvent = null;
		}

		public void ClearNetworkErrorEvent()
		{
			this.NetworkErrorEvent = null;
		}

		public void ClearNetworkReceiveEvent()
		{
			this.NetworkReceiveEvent = null;
		}

		public void ClearNetworkReceiveUnconnectedEvent()
		{
			this.NetworkReceiveUnconnectedEvent = null;
		}

		public void ClearNetworkLatencyUpdateEvent()
		{
			this.NetworkLatencyUpdateEvent = null;
		}

		public void ClearConnectionRequestEvent()
		{
			this.ConnectionRequestEvent = null;
		}

		public void ClearDeliveryEvent()
		{
			this.DeliveryEvent = null;
		}

		public void ClearNtpResponseEvent()
		{
			this.NtpResponseEvent = null;
		}

		public void ClearPeerAddressChangedEvent()
		{
			this.PeerAddressChangedEvent = null;
		}

		void INetEventListener.OnPeerConnected(NetPeer peer)
		{
			if (this.PeerConnectedEvent != null)
			{
				this.PeerConnectedEvent(peer);
			}
		}

		void INetEventListener.OnPeerDisconnected(NetPeer peer, DisconnectInfo disconnectInfo)
		{
			if (this.PeerDisconnectedEvent != null)
			{
				this.PeerDisconnectedEvent(peer, disconnectInfo);
			}
		}

		void INetEventListener.OnNetworkError(IPEndPoint endPoint, SocketError socketErrorCode)
		{
			if (this.NetworkErrorEvent != null)
			{
				this.NetworkErrorEvent(endPoint, socketErrorCode);
			}
		}

		void INetEventListener.OnNetworkReceive(NetPeer peer, NetPacketReader reader, byte channelNumber, DeliveryMethod deliveryMethod)
		{
			if (this.NetworkReceiveEvent != null)
			{
				this.NetworkReceiveEvent(peer, reader, channelNumber, deliveryMethod);
			}
		}

		void INetEventListener.OnNetworkReceiveUnconnected(IPEndPoint remoteEndPoint, NetPacketReader reader, UnconnectedMessageType messageType)
		{
			if (this.NetworkReceiveUnconnectedEvent != null)
			{
				this.NetworkReceiveUnconnectedEvent(remoteEndPoint, reader, messageType);
			}
		}

		void INetEventListener.OnNetworkLatencyUpdate(NetPeer peer, int latency)
		{
			if (this.NetworkLatencyUpdateEvent != null)
			{
				this.NetworkLatencyUpdateEvent(peer, latency);
			}
		}

		void INetEventListener.OnConnectionRequest(ConnectionRequest request)
		{
			if (this.ConnectionRequestEvent != null)
			{
				this.ConnectionRequestEvent(request);
			}
		}

		void IDeliveryEventListener.OnMessageDelivered(NetPeer peer, object userData)
		{
			if (this.DeliveryEvent != null)
			{
				this.DeliveryEvent(peer, userData);
			}
		}

		void INtpEventListener.OnNtpResponse(NtpPacket packet)
		{
			if (this.NtpResponseEvent != null)
			{
				this.NtpResponseEvent(packet);
			}
		}

		void IPeerAddressChangedListener.OnPeerAddressChanged(NetPeer peer, IPEndPoint previousAddress)
		{
			if (this.PeerAddressChangedEvent != null)
			{
				this.PeerAddressChangedEvent(peer, previousAddress);
			}
		}
	}
	internal sealed class NetConnectRequestPacket
	{
		public const int HeaderSize = 18;

		public readonly long ConnectionTime;

		public byte ConnectionNumber;

		public readonly byte[] TargetAddress;

		public readonly NetDataReader Data;

		public readonly int PeerId;

		private NetConnectRequestPacket(long connectionTime, byte connectionNumber, int localId, byte[] targetAddress, NetDataReader data)
		{
			ConnectionTime = connectionTime;
			ConnectionNumber = connectionNumber;
			TargetAddress = targetAddress;
			Data = data;
			PeerId = localId;
		}

		public static int GetProtocolId(NetPacket packet)
		{
			return BitConverter.ToInt32(packet.RawData, 1);
		}

		public static NetConnectRequestPacket FromData(NetPacket packet)
		{
			if (packet.ConnectionNumber >= 4)
			{
				return null;
			}
			long connectionTime = BitConverter.ToInt64(packet.RawData, 5);
			int localId = BitConverter.ToInt32(packet.RawData, 13);
			int num = packet.RawData[17];
			if (num != 16 && num != 28)
			{
				return null;
			}
			byte[] array = new byte[num];
			Buffer.BlockCopy(packet.RawData, 18, array, 0, num);
			NetDataReader netDataReader = new NetDataReader(null, 0, 0);
			if (packet.Size > 18 + num)
			{
				netDataReader.SetSource(packet.RawData, 18 + num, packet.Size);
			}
			return new NetConnectRequestPacket(connectionTime, packet.ConnectionNumber, localId, array, netDataReader);
		}

		public static NetPacket Make(NetDataWriter connectData, SocketAddress addressBytes, long connectTime, int localId)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.ConnectRequest, connectData.Length + addressBytes.Size);
			FastBitConverter.GetBytes(netPacket.RawData, 1, 13);
			FastBitConverter.GetBytes(netPacket.RawData, 5, connectTime);
			FastBitConverter.GetBytes(netPacket.RawData, 13, localId);
			netPacket.RawData[17] = (byte)addressBytes.Size;
			for (int i = 0; i < addressBytes.Size; i++)
			{
				netPacket.RawData[18 + i] = addressBytes[i];
			}
			Buffer.BlockCopy(connectData.Data, 0, netPacket.RawData, 18 + addressBytes.Size, connectData.Length);
			return netPacket;
		}
	}
	internal sealed class NetConnectAcceptPacket
	{
		public const int Size = 15;

		public readonly long ConnectionTime;

		public readonly byte ConnectionNumber;

		public readonly int PeerId;

		public readonly bool PeerNetworkChanged;

		private NetConnectAcceptPacket(long connectionTime, byte connectionNumber, int peerId, bool peerNetworkChanged)
		{
			ConnectionTime = connectionTime;
			ConnectionNumber = connectionNumber;
			PeerId = peerId;
			PeerNetworkChanged = peerNetworkChanged;
		}

		public static NetConnectAcceptPacket FromData(NetPacket packet)
		{
			if (packet.Size != 15)
			{
				return null;
			}
			long connectionTime = BitConverter.ToInt64(packet.RawData, 1);
			byte b = packet.RawData[9];
			if (b >= 4)
			{
				return null;
			}
			byte b2 = packet.RawData[10];
			if (b2 > 1)
			{
				return null;
			}
			int num = BitConverter.ToInt32(packet.RawData, 11);
			if (num < 0)
			{
				return null;
			}
			return new NetConnectAcceptPacket(connectionTime, b, num, b2 == 1);
		}

		public static NetPacket Make(long connectTime, byte connectNum, int localPeerId)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.ConnectAccept, 0);
			FastBitConverter.GetBytes(netPacket.RawData, 1, connectTime);
			netPacket.RawData[9] = connectNum;
			FastBitConverter.GetBytes(netPacket.RawData, 11, localPeerId);
			return netPacket;
		}

		public static NetPacket MakeNetworkChanged(NetPeer peer)
		{
			NetPacket netPacket = new NetPacket(PacketProperty.PeerNotFound, 14);
			FastBitConverter.GetBytes(netPacket.RawData, 1, peer.ConnectTime);
			netPacket.RawData[9] = peer.ConnectionNum;
			netPacket.RawData[10] = 1;
			FastBitConverter.GetBytes(netPacket.RawData, 11, peer.RemoteId);
			return netPacket;
		}
	}
	internal static class NativeSocket
	{
		private static class WinSock
		{
			private const string LibName = "ws2_32.dll";

			[DllImport("ws2_32.dll", SetLastError = true)]
			public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize);

			[DllImport("ws2_32.dll", SetLastError = true)]
			internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize);
		}

		private static class UnixSock
		{
			private const string LibName = "libc";

			[DllImport("libc", SetLastError = true)]
			public static extern int recvfrom(IntPtr socketHandle, [In][Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In][Out] ref int socketAddressSize);

			[DllImport("libc", SetLastError = true)]
			internal unsafe static extern int sendto(IntPtr socketHandle, byte* pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize);
		}

		public static readonly bool IsSupported;

		public static readonly bool UnixMode;

		public const int IPv4AddrSize = 16;

		public const int IPv6AddrSize = 28;

		public const int AF_INET = 2;

		public const int AF_INET6 = 10;

		private static readonly Dictionary<int, SocketError> NativeErrorToSocketError;

		static NativeSocket()
		{
			IsSupported = false;
			UnixMode = false;
			NativeErrorToSocketError = new Dictionary<int, SocketError>
			{
				{
					13,
					SocketError.AccessDenied
				},
				{
					98,
					SocketError.AddressAlreadyInUse
				},
				{
					99,
					SocketError.AddressNotAvailable
				},
				{
					97,
					SocketError.AddressFamilyNotSupported
				},
				{
					11,
					SocketError.WouldBlock
				},
				{
					114,
					SocketError.AlreadyInProgress
				},
				{
					9,
					SocketError.OperationAborted
				},
				{
					125,
					SocketError.OperationAborted
				},
				{
					103,
					SocketError.ConnectionAborted
				},
				{
					111,
					SocketError.ConnectionRefused
				},
				{
					104,
					SocketError.ConnectionReset
				},
				{
					89,
					SocketError.DestinationAddressRequired
				},
				{
					14,
					SocketError.Fault
				},
				{
					112,
					SocketError.HostDown
				},
				{
					6,
					SocketError.HostNotFound
				},
				{
					113,
					SocketError.HostUnreachable
				},
				{
					115,
					SocketError.InProgress
				},
				{
					4,
					SocketError.Interrupted
				},
				{
					22,
					SocketError.InvalidArgument
				},
				{
					106,
					SocketError.IsConnected
				},
				{
					24,
					SocketError.TooManyOpenSockets
				},
				{
					90,
					SocketError.MessageSize
				},
				{
					100,
					SocketError.NetworkDown
				},
				{
					102,
					SocketError.NetworkReset
				},
				{
					101,
					SocketError.NetworkUnreachable
				},
				{
					23,
					SocketError.TooManyOpenSockets
				},
				{
					105,
					SocketError.NoBufferSpaceAvailable
				},
				{
					61,
					SocketError.NoData
				},
				{
					2,
					SocketError.AddressNotAvailable
				},
				{
					92,
					SocketError.ProtocolOption
				},
				{
					107,
					SocketError.NotConnected
				},
				{
					88,
					SocketError.NotSocket
				},
				{
					3440,
					SocketError.OperationNotSupported
				},
				{
					1,
					SocketError.AccessDenied
				},
				{
					32,
					SocketError.Shutdown
				},
				{
					96,
					SocketError.ProtocolFamilyNotSupported
				},
				{
					93,
					SocketError.ProtocolNotSupported
				},
				{
					91,
					SocketError.ProtocolType
				},
				{
					94,
					SocketError.SocketNotSupported
				},
				{
					108,
					SocketError.Disconnecting
				},
				{
					110,
					SocketError.TimedOut
				},
				{
					0,
					SocketError.Success
				}
			};
			if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
			{
				IsSupported = true;
				UnixMode = true;
			}
			else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				IsSupported = true;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static int RecvFrom(IntPtr socketHandle, byte[] pinnedBuffer, int len, byte[] socketAddress, ref int socketAddressSize)
		{
			if (!UnixMode)
			{
				return WinSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize);
			}
			return UnixSock.recvfrom(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, ref socketAddressSize);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public unsafe static int SendTo(IntPtr socketHandle, byte* pinnedBuffer, int len, byte[] socketAddress, int socketAddressSize)
		{
			if (!UnixMode)
			{
				return WinSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize);
			}
			return UnixSock.sendto(socketHandle, pinnedBuffer, len, SocketFlags.None, socketAddress, socketAddressSize);
		}

		public static SocketError GetSocketError()
		{
			int lastWin32Error = Marshal.GetLastWin32Error();
			if (UnixMode)
			{
				if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value))
				{
					return SocketError.SocketError;
				}
				return value;
			}
			return (SocketError)lastWin32Error;
		}

		public static SocketException GetSocketException()
		{
			int lastWin32Error = Marshal.GetLastWin32Error();
			if (UnixMode)
			{
				if (!NativeErrorToSocketError.TryGetValue(lastWin32Error, out var value))
				{
					return new SocketException(-1);
				}
				return new SocketException((int)value);
			}
			return new SocketException(lastWin32Error);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint)
		{
			if (!UnixMode)
			{
				return (short)remoteEndPoint.AddressFamily;
			}
			return (short)((remoteEndPoint.AddressFamily == AddressFamily.InterNetwork) ? 2 : 10);
		}
	}
	public enum NatAddressType
	{
		Internal,
		External
	}
	public interface INatPunchListener
	{
		void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);

		void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);
	}
	public class EventBasedNatPunchListener : INatPunchListener
	{
		public delegate void OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token);

		public delegate void OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token);

		public event OnNatIntroductionRequest NatIntroductionRequest;

		public event OnNatIntroductionSuccess NatIntroductionSuccess;

		void INatPunchListener.OnNatIntroductionRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, string token)
		{
			if (this.NatIntroductionRequest != null)
			{
				this.NatIntroductionRequest(localEndPoint, remoteEndPoint, token);
			}
		}

		void INatPunchListener.OnNatIntroductionSuccess(IPEndPoint targetEndPoint, NatAddressType type, string token)
		{
			if (this.NatIntroductionSuccess != null)
			{
				this.NatIntroductionSuccess(targetEndPoint, type, token);
			}
		}
	}
	public sealed class NatPunchModule
	{
		private struct RequestEventData
		{
			public IPEndPoint LocalEndPoint;

			public IPEndPoint RemoteEndPoint;

			public string Token;
		}

		private struct SuccessEventData
		{
			public IPEndPoint TargetEndPoint;

			public NatAddressType Type;

			public string Token;
		}

		private class NatIntroduceRequestPacket
		{
			public IPEndPoint Internal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private class NatIntroduceResponsePacket
		{
			public IPEndPoint Internal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public IPEndPoint External
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private class NatPunchPacket
		{
			public string Token
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}

			public bool IsExternal
			{
				[Preserve]
				get;
				[Preserve]
				set;
			}
		}

		private readonly NetManager _socket;

		private readonly ConcurrentQueue<RequestEventData> _requestEvents = new ConcurrentQueue<RequestEventData>();

		private readonly ConcurrentQueue<SuccessEventData> _successEvents = new ConcurrentQueue<SuccessEventData>();

		private readonly NetDataReader _cacheReader = new NetDataReader();

		private readonly NetDataWriter _cacheWriter = new NetDataWriter();

		private readonly NetPacketProcessor _netPacketProcessor = new NetPacketProcessor(256);

		private INatPunchListener _natPunchListener;

		public const int MaxTokenLength = 256;

		public bool UnsyncedEvents;

		internal NatPunchModule(NetManager socket)
		{
			_socket = socket;
			_netPacketProcessor.SubscribeReusable<NatIntroduceResponsePacket>(OnNatIntroductionResponse);
			_netPacketProcessor.SubscribeReusable<NatIntroduceRequestPacket, IPEndPoint>(OnNatIntroductionRequest);
			_netPacketProcessor.SubscribeReusable<NatPunchPacket, IPEndPoint>(OnNatPunch);
		}

		internal void ProcessMessage(IPEndPoint senderEndPoint, NetPacket packet)
		{
			lock (_cacheReader)
			{
				_cacheReader.SetSource(packet.RawData, 1, packet.Size);
				_netPacketProcessor.ReadAllPackets(_cacheReader, senderEndPoint);
			}
		}

		public void Init(INatPunchListener listener)
		{
			_natPunchListener = listener;
		}

		private void Send<T>(T packet, IPEndPoint target) where T : class, new()
		{
			_cacheWriter.Reset();
			_cacheWriter.Put((byte)16);
			_netPacketProcessor.Write(_cacheWriter, packet);
			_socket.SendRaw(_cacheWriter.Data, 0, _cacheWriter.Length, target);
		}

		public void NatIntroduce(IPEndPoint hostInternal, IPEndPoint hostExternal, IPEndPoint clientInternal, IPEndPoint clientExternal, string additionalInfo)
		{
			NatIntroduceResponsePacket natIntroduceResponsePacket = new NatIntroduceResponsePacket
			{
				Token = additionalInfo
			};
			natIntroduceResponsePacket.Internal = hostInternal;
			natIntroduceResponsePacket.External = hostExternal;
			Send(natIntroduceResponsePacket, clientExternal);
			natIntroduceResponsePacket.Internal = clientInternal;
			natIntroduceResponsePacket.External = clientExternal;
			Send(natIntroduceResponsePacket, hostExternal);
		}

		public void PollEvents()
		{
			if (!UnsyncedEvents && _natPunchListener != null && (!_successEvents.IsEmpty || !_requestEvents.IsEmpty))
			{
				SuccessEventData result;
				while (_successEvents.TryDequeue(out result))
				{
					_natPunchListener.OnNatIntroductionSuccess(result.TargetEndPoint, result.Type, result.Token);
				}
				RequestEventData result2;
				while (_requestEvents.TryDequeue(out result2))
				{
					_natPunchListener.OnNatIntroductionRequest(result2.LocalEndPoint, result2.RemoteEndPoint, result2.Token);
				}
			}
		}

		public void SendNatIntroduceRequest(string host, int port, string additionalInfo)
		{
			SendNatIntroduceRequest(NetUtils.MakeEndPoint(host, port), additionalInfo);
		}

		public void SendNatIntroduceRequest(IPEndPoint masterServerEndPoint, string additionalInfo)
		{
			string localIp = NetUtils.GetLocalIp(LocalAddrType.IPv4);
			if (string.IsNullOrEmpty(localIp) || masterServerEndPoint.AddressFamily == AddressFamily.InterNetworkV6)
			{
				localIp = NetUtils.GetLocalIp(LocalAddrType.IPv6);
			}
			Send(new NatIntroduceRequestPacket
			{
				Internal = NetUtils.MakeEndPoint(localIp, _socket.LocalPort),
				Token = additionalInfo
			}, masterServerEndPoint);
		}

		private void OnNatIntroductionRequest(NatIntroduceRequestPacket req, IPEndPoint senderEndPoint)
		{
			if (UnsyncedEvents)
			{
				_natPunchListener.OnNatIntroductionRequest(req.Internal, senderEndPoint, req.Token);
				return;
			}
			_requestEvents.Enqueue(new RequestEventData
			{
				LocalEndPoint = req.Internal,
				RemoteEndPoint = senderEndPoint,
				Token = req.Token
			});
		}

		private void OnNatIntroductionResponse(NatIntroduceResponsePacket req)
		{
			NatPunchPacket natPunchPacket = new NatPunchPacket
			{
				Token = req.Token
			};
			Send(natPunchPacket, req.Internal);
			_socket.Ttl = 2;
			_socket.SendRaw(new byte[1] { 17 }, 0, 1, req.External);
			_socket.Ttl = 255;
			natPunchPacket.IsExternal = true;
			Send(natPunchPacket, req.External);
		}

		private void OnNatPunch(NatPunchPacket req, IPEndPoint senderEndPoint)
		{
			if (UnsyncedEvents)
			{
				_natPunchListener.OnNatIntroductionSuccess(senderEndPoint, req.IsExternal ? NatAddressType.External : NatAddressType.Internal, req.Token);
				return;
			}
			_successEvents.Enqueue(new SuccessEventData
			{
				TargetEndPoint = senderEndPoint,
				Type = (req.IsExternal ? NatAddressType.External : NatAddressType.Internal),
				Token = req.Token
			});
		}
	}
	public enum DeliveryMethod : byte
	{
		Unreliable = 4,
		ReliableUnordered = 0,
		Sequenced = 1,
		ReliableOrdered = 2,
		ReliableSequenced = 3
	}
	public static class NetConstants
	{
		public const int DefaultWindowSize = 64;

		public const int SocketBufferSize = 1048576;

		public const int SocketTTL = 255;

		public const int HeaderSize = 1;

		public const int ChanneledHeaderSize = 4;

		public const int FragmentHeaderSize = 6;

		public const int FragmentedHeaderTotalSize = 10;

		public const ushort MaxSequence = 32768;

		public const ushort HalfMaxSequence = 16384;

		internal const int ProtocolId = 13;

		internal const int MaxUdpHeaderSize = 68;

		internal const int ChannelTypeCount = 4;

		internal static readonly int[] PossibleMtu = new int[6] { 1024, 1164, 1392, 1404, 1424, 1432 };

		public static readonly int InitialMtu = PossibleMtu[0];

		public static readonly int MaxPacketSize = PossibleMtu[PossibleMtu.Length - 1];

		public static readonly int MaxUnreliableDataSize = MaxPacketSize - 1;

		public const byte MaxConnectionNumber = 4;
	}
	public class InvalidPacketException : ArgumentException
	{
		public InvalidPacketException(string message)
			: base(message)
		{
		}
	}
	public class TooBigPacketException : InvalidPacketException
	{
		public TooBigPacketException(string message)
			: base(message)
		{
		}
	}
	public enum NetLogLevel
	{
		Warning,
		Error,
		Trace,
		Info
	}
	public interface INetLogger
	{
		void WriteNet(NetLogLevel level, string str, params object[] args);
	}
	public static class NetDebug
	{
		public static INetLogger Logger = null;

		private static readonly object DebugLogLock = new object();

		private static void WriteLogic(NetLogLevel logLevel, string str, params object[] args)
		{
			lock (DebugLogLock)
			{
				if (Logger == null)
				{
					Console.WriteLine(str, args);
				}
				else
				{
					Logger.WriteNet(logLevel, str, args);
				}
			}
		}

		[Conditional("DEBUG_MESSAGES")]
		internal static void Write(string str)
		{
			WriteLogic(NetLogLevel.Trace, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		internal static void Write(NetLogLevel level, string str)
		{
			WriteLogic(level, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		[Conditional("DEBUG")]
		internal static void WriteForce(string str)
		{
			WriteLogic(NetLogLevel.Trace, str);
		}

		[Conditional("DEBUG_MESSAGES")]
		[Conditional("DEBUG")]
		internal static void WriteForce(NetLogLevel level, string str)
		{
			WriteLogic(level, str);
		}

		internal static void WriteError(string str)
		{
			WriteLogic(NetLogLevel.Error, str);
		}
	}
	public sealed class NetPacketReader : NetDataReader
	{
		private NetPacket _packet;

		private readonly NetManager _manager;

		private readonly NetEvent _evt;

		internal NetPacketReader(NetManager manager, NetEvent evt)
		{
			_manager = manager;
			_evt = evt;
		}

		internal void SetSource(NetPacket packet, int headerSize)
		{
			if (packet != null)
			{
				_packet = packet;
				SetSource(packet.RawData, headerSize, packet.Size);
			}
		}

		internal void RecycleInternal()
		{
			Clear();
			if (_packet != null)
			{
				_manager.PoolRecycle(_packet);
			}
			_packet = null;
			_manager.RecycleEvent(_evt);
		}

		public void Recycle()
		{
			if (!_manager.AutoRecycle)
			{
				RecycleInternal();
			}
		}
	}
	internal sealed class NetEvent
	{
		public enum EType
		{
			Connect,
			Disconnect,
			Receive,
			ReceiveUnconnected,
			Error,
			ConnectionLatencyUpdated,
			Broadcast,
			ConnectionRequest,
			MessageDelivered,
			PeerAddressChanged
		}

		public NetEvent Next;

		public EType Type;

		public NetPeer Peer;

		public IPEndPoint RemoteEndPoint;

		public object UserData;

		public int Latency;

		public SocketError ErrorCode;

		public DisconnectReason DisconnectReason;

		public ConnectionRequest ConnectionRequest;

		public DeliveryMethod DeliveryMethod;

		public byte ChannelNumber;

		public readonly NetPacketReader DataReader;

		public NetEvent(NetManager manager)
		{
			DataReader = new NetPacketReader(manager, this);
		}
	}
	public class NetManager : IEnumerable<NetPeer>, IEnumerable
	{
		public struct NetPeerEnumerator : IEnumerator<NetPeer>, IDisposable, IEnumerator
		{
			private readonly NetPeer _initialPeer;

			private NetPeer _p;

			public NetPeer Current => _p;

			object IEnumerator.Current => _p;

			public NetPeerEnumerator(NetPeer p)
			{
				_initialPeer = p;
				_p = null;
			}

			public void Dispose()
			{
			}

			public bool MoveNext()
			{
				_p = ((_p == null) ? _initialPeer : _p.NextPeer);
				return _p != null;
			}

			public void Reset()
			{
				throw new NotSupportedException();
			}
		}

		private struct IncomingData
		{
			public NetPacket Data;

			public IPEndPoint EndPoint;

			public DateTime TimeWhenGet;
		}

		private struct Slot
		{
			internal int HashCode;

			internal int Next;

			internal NetPeer Value;
		}

		private readonly List<IncomingData> _pingSimulationList = new List<IncomingData>();

		private readonly Random _randomGenerator = new Random();

		private const int MinLatencyThreshold = 5;

		private Thread _logicThread;

		private bool _manualMode;

		private readonly AutoResetEvent _updateTriggerEvent = new AutoResetEvent(initialState: true);

		private NetEvent _pendingEventHead;

		private NetEvent _pendingEventTail;

		private NetEvent _netEventPoolHead;

		private readonly INetEventListener _netEventListener;

		private readonly IDeliveryEventListener _deliveryEventListener;

		private readonly INtpEventListener _ntpEventListener;

		private readonly IPeerAddressChangedListener _peerAddressChangedListener;

		private readonly Dictionary<IPEndPoint, ConnectionRequest> _requestsDict = new Dictionary<IPEndPoint, ConnectionRequest>();

		private readonly ConcurrentDictionary<IPEndPoint, NtpRequest> _ntpRequests = new ConcurrentDictionary<IPEndPoint, NtpRequest>();

		private long _connectedPeersCount;

		private readonly List<NetPeer> _connectedPeerListCache = new List<NetPeer>();

		private readonly PacketLayerBase _extraPacketLayer;

		private int _lastPeerId;

		private ConcurrentQueue<int> _peerIds = new ConcurrentQueue<int>();

		private byte _channelsCount = 1;

		private readonly object _eventLock = new object();

		private bool _dropPacket;

		public bool UnconnectedMessagesEnabled;

		public bool NatPunchEnabled;

		public int UpdateTime = 15;

		public int PingInterval = 1000;

		public int DisconnectTimeout = 5000;

		public bool SimulatePacketLoss;

		public bool SimulateLatency;

		public int SimulationPacketLossChance = 10;

		public int SimulationMinLatency = 30;

		public int SimulationMaxLatency = 100;

		public bool UnsyncedEvents;

		public bool UnsyncedReceiveEvent;

		public bool UnsyncedDeliveryEvent;

		public bool BroadcastReceiveEnabled;

		public int ReconnectDelay = 500;

		public int MaxConnectAttempts = 10;

		public bool ReuseAddress;

		public bool DontRoute;

		public readonly NetStatistics Statistics = new NetStatistics();

		public bool EnableStatistics;

		public readonly NatPunchModule NatPunchModule;

		public bool AutoRecycle;

		public bool IPv6Enabled = true;

		public int MtuOverride;

		public bool MtuDiscovery;

		public bool UseNativeSockets;

		public bool DisconnectOnUnreachable;

		public bool AllowPeerAddressChange;

		private const int MaxPrimeArrayLength = 2147483587;

		private const int HashPrime = 101;

		private const int Lower31BitMask = int.MaxValue;

		private static readonly int[] Primes;

		private int[] _buckets;

		private Slot[] _slots;

		private int _count;

		private int _lastIndex;

		private int _freeList = -1;

		private NetPeer[] _peersArray = new NetPeer[32];

		private readonly ReaderWriterLockSlim _peersLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

		private volatile NetPeer _headPeer;

		private NetPacket _poolHead;

		private int _poolCount;

		private readonly object _poolLock = new object();

		public int PacketPoolSize = 1000;

		private const int ReceivePollingTime = 500000;

		private Socket _udpSocketv4;

		private Socket _udpSocketv6;

		private Thread _receiveThread;

		private IPEndPoint _bufferEndPointv4;

		private IPEndPoint _bufferEndPointv6;

		private const int SioUdpConnreset = -1744830452;

		private static readonly IPAddress MulticastAddressV6;

		public static readonly bool IPv6Support;

		internal bool NotConnected;

		public bool IsRunning { get; private set; }

		public int LocalPort { get; private set; }

		public NetPeer FirstPeer => _headPeer;

		public byte ChannelsCount
		{
			get
			{
				return _channelsCount;
			}
			set
			{
				if (value < 1 || value > 64)
				{
					throw new ArgumentException("Channels count must be between 1 and 64");
				}
				_channelsCount = value;
			}
		}

		public List<NetPeer> ConnectedPeerList
		{
			get
			{
				GetPeersNonAlloc(_connectedPeerListCache, ConnectionState.Connected);
				return _connectedPeerListCache;
			}
		}

		public int ConnectedPeersCount => (int)Interlocked.Read(ref _connectedPeersCount);

		public int ExtraPacketSizeForLayer => _extraPacketLayer?.ExtraPacketSizeForLayer ?? 0;

		public int PoolCount => _poolCount;

		public short Ttl
		{
			get
			{
				return _udpSocketv4.Ttl;
			}
			internal set
			{
				_udpSocketv4.Ttl = value;
			}
		}

		public NetManager(INetEventListener listener, PacketLayerBase extraPacketLayer = null)
		{
			_netEventListener = listener;
			_deliveryEventListener = listener as IDeliveryEventListener;
			_ntpEventListener = listener as INtpEventListener;
			_peerAddressChangedListener = listener as IPeerAddressChangedListener;
			NatPunchModule = new NatPunchModule(this);
			_extraPacketLayer = extraPacketLayer;
		}

		internal void ConnectionLatencyUpdated(NetPeer fromPeer, int latency)
		{
			CreateEvent(NetEvent.EType.ConnectionLatencyUpdated, fromPeer, null, SocketError.Success, latency, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
		}

		internal void MessageDelivered(NetPeer fromPeer, object userData)
		{
			if (_deliveryEventListener != null)
			{
				CreateEvent(NetEvent.EType.MessageDelivered, fromPeer, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, null, userData);
			}
		}

		internal void DisconnectPeerForce(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, NetPacket eventData)
		{
			DisconnectPeer(peer, reason, socketErrorCode, force: true, null, 0, 0, eventData);
		}

		private void DisconnectPeer(NetPeer peer, DisconnectReason reason, SocketError socketErrorCode, bool force, byte[] data, int start, int count, NetPacket eventData)
		{
			switch (peer.Shutdown(data, start, count, force))
			{
			case ShutdownResult.None:
				return;
			case ShutdownResult.WasConnected:
				Interlocked.Decrement(ref _connectedPeersCount);
				break;
			}
			CreateEvent(NetEvent.EType.Disconnect, peer, null, socketErrorCode, 0, reason, null, DeliveryMethod.Unreliable, 0, eventData);
		}

		private void CreateEvent(NetEvent.EType type, NetPeer peer = null, IPEndPoint remoteEndPoint = null, SocketError errorCode = SocketError.Success, int latency = 0, DisconnectReason disconnectReason = DisconnectReason.ConnectionFailed, ConnectionRequest connectionRequest = null, DeliveryMethod deliveryMethod = DeliveryMethod.Unreliable, byte channelNumber = 0, NetPacket readerSource = null, object userData = null)
		{
			bool flag = UnsyncedEvents;
			switch (type)
			{
			case NetEvent.EType.Connect:
				Interlocked.Increment(ref _connectedPeersCount);
				break;
			case NetEvent.EType.MessageDelivered:
				flag = UnsyncedDeliveryEvent;
				break;
			}
			NetEvent netEvent;
			lock (_eventLock)
			{
				netEvent = _netEventPoolHead;
				if (netEvent == null)
				{
					netEvent = new NetEvent(this);
				}
				else
				{
					_netEventPoolHead = netEvent.Next;
				}
			}
			netEvent.Next = null;
			netEvent.Type = type;
			netEvent.DataReader.SetSource(readerSource, readerSource?.GetHeaderSize() ?? 0);
			netEvent.Peer = peer;
			netEvent.RemoteEndPoint = remoteEndPoint;
			netEvent.Latency = latency;
			netEvent.ErrorCode = errorCode;
			netEvent.DisconnectReason = disconnectReason;
			netEvent.ConnectionRequest = connectionRequest;
			netEvent.DeliveryMethod = deliveryMethod;
			netEvent.ChannelNumber = channelNumber;
			netEvent.UserData = userData;
			if (flag || _manualMode)
			{
				ProcessEvent(netEvent);
				return;
			}
			lock (_eventLock)
			{
				if (_pendingEventTail == null)
				{
					_pendingEventHead = netEvent;
				}
				else
				{
					_pendingEventTail.Next = netEvent;
				}
				_pendingEventTail = netEvent;
			}
		}

		private void ProcessEvent(NetEvent evt)
		{
			bool isNull = evt.DataReader.IsNull;
			switch (evt.Type)
			{
			case NetEvent.EType.Connect:
				_netEventListener.OnPeerConnected(evt.Peer);
				break;
			case NetEvent.EType.Disconnect:
			{
				DisconnectInfo disconnectInfo = default(DisconnectInfo);
				disconnectInfo.Reason = evt.DisconnectReason;
				disconnectInfo.AdditionalData = evt.DataReader;
				disconnectInfo.SocketErrorCode = evt.ErrorCode;
				DisconnectInfo disconnectInfo2 = disconnectInfo;
				_netEventListener.OnPeerDisconnected(evt.Peer, disconnectInfo2);
				break;
			}
			case NetEvent.EType.Receive:
				_netEventListener.OnNetworkReceive(evt.Peer, evt.DataReader, evt.ChannelNumber, evt.DeliveryMethod);
				break;
			case NetEvent.EType.ReceiveUnconnected:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.BasicMessage);
				break;
			case NetEvent.EType.Broadcast:
				_netEventListener.OnNetworkReceiveUnconnected(evt.RemoteEndPoint, evt.DataReader, UnconnectedMessageType.Broadcast);
				break;
			case NetEvent.EType.Error:
				_netEventListener.OnNetworkError(evt.RemoteEndPoint, evt.ErrorCode);
				break;
			case NetEvent.EType.ConnectionLatencyUpdated:
				_netEventListener.OnNetworkLatencyUpdate(evt.Peer, evt.Latency);
				break;
			case NetEvent.EType.ConnectionRequest:
				_netEventListener.OnConnectionRequest(evt.ConnectionRequest);
				break;
			case NetEvent.EType.MessageDelivered:
				_deliveryEventListener.OnMessageDelivered(evt.Peer, evt.UserData);
				break;
			case NetEvent.EType.PeerAddressChanged:
			{
				_peersLock.EnterUpgradeableReadLock();
				IPEndPoint iPEndPoint = null;
				if (ContainsPeer(evt.Peer))
				{
					_peersLock.EnterWriteLock();
					RemovePeerFromSet(evt.Peer);
					iPEndPoint = new IPEndPoint(evt.Peer.Address, evt.Peer.Port);
					evt.Peer.FinishEndPointChange(evt.RemoteEndPoint);
					AddPeerToSet(evt.Peer);
					_peersLock.ExitWriteLock();
				}
				_peersLock.ExitUpgradeableReadLock();
				if (iPEndPoint != null && _peerAddressChangedListener != null)
				{
					_peerAddressChangedListener.OnPeerAddressChanged(evt.Peer, iPEndPoint);
				}
				break;
			}
			}
			if (isNull)
			{
				RecycleEvent(evt);
			}
			else if (AutoRecycle)
			{
				evt.DataReader.RecycleInternal();
			}
		}

		internal void RecycleEvent(NetEvent evt)
		{
			evt.Peer = null;
			evt.ErrorCode = SocketError.Success;
			evt.RemoteEndPoint = null;
			evt.ConnectionRequest = null;
			lock (_eventLock)
			{
				evt.Next = _netEventPoolHead;
				_netEventPoolHead = evt;
			}
		}

		private void UpdateLogic()
		{
			List<NetPeer> list = new List<NetPeer>();
			Stopwatch stopwatch = new Stopwatch();
			stopwatch.Start();
			while (IsRunning)
			{
				try
				{
					float num = (float)((double)stopwatch.ElapsedTicks / (double)Stopwatch.Frequency * 1000.0);
					num = ((num <= 0f) ? 0.001f : num);
					stopwatch.Restart();
					for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
					{
						if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout)
						{
							list.Add(netPeer);
						}
						else
						{
							netPeer.Update(num);
						}
					}
					if (list.Count > 0)
					{
						_peersLock.EnterWriteLock();
						for (int i = 0; i < list.Count; i++)
						{
							RemovePeer(list[i], enableWriteLock: false);
						}
						_peersLock.ExitWriteLock();
						list.Clear();
					}
					ProcessNtpRequests(num);
					int num2 = UpdateTime - (int)stopwatch.ElapsedMilliseconds;
					if (num2 > 0)
					{
						_updateTriggerEvent.WaitOne(num2);
					}
				}
				catch (ThreadAbortException)
				{
					return;
				}
				catch (Exception ex2)
				{
					NetDebug.WriteError("[NM] LogicThread error: " + ex2);
				}
			}
			stopwatch.Stop();
		}

		[Conditional("DEBUG")]
		private void ProcessDelayedPackets()
		{
			if (!SimulateLatency)
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			lock (_pingSimulationList)
			{
				for (int i = 0; i < _pingSimulationList.Count; i++)
				{
					IncomingData incomingData = _pingSimulationList[i];
					if (incomingData.TimeWhenGet <= utcNow)
					{
						HandleMessageReceived(incomingData.Data, incomingData.EndPoint);
						_pingSimulationList.RemoveAt(i);
						i--;
					}
				}
			}
		}

		private void ProcessNtpRequests(float elapsedMilliseconds)
		{
			List<IPEndPoint> list = null;
			foreach (KeyValuePair<IPEndPoint, NtpRequest> ntpRequest in _ntpRequests)
			{
				ntpRequest.Value.Send(_udpSocketv4, elapsedMilliseconds);
				if (ntpRequest.Value.NeedToKill)
				{
					if (list == null)
					{
						list = new List<IPEndPoint>();
					}
					list.Add(ntpRequest.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (IPEndPoint item in list)
			{
				_ntpRequests.TryRemove(item, out var _);
			}
		}

		public void ManualUpdate(float elapsedMilliseconds)
		{
			if (!_manualMode)
			{
				return;
			}
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if (netPeer.ConnectionState == ConnectionState.Disconnected && netPeer.TimeSinceLastPacket > (float)DisconnectTimeout)
				{
					RemovePeer(netPeer, enableWriteLock: false);
				}
				else
				{
					netPeer.Update(elapsedMilliseconds);
				}
			}
			ProcessNtpRequests(elapsedMilliseconds);
		}

		internal NetPeer OnConnectionSolved(ConnectionRequest request, byte[] rejectData, int start, int length)
		{
			NetPeer actualValue = null;
			if (request.Result == ConnectionRequestResult.RejectForce)
			{
				if (rejectData != null && length > 0)
				{
					NetPacket netPacket = PoolGetWithProperty(PacketProperty.Disconnect, length);
					netPacket.ConnectionNumber = request.InternalPacket.ConnectionNumber;
					FastBitConverter.GetBytes(netPacket.RawData, 1, request.InternalPacket.ConnectionTime);
					if (netPacket.Size >= NetConstants.PossibleMtu[0])
					{
						NetDebug.WriteError("[Peer] Disconnect additional data size more than MTU!");
					}
					else
					{
						Buffer.BlockCopy(rejectData, start, netPacket.RawData, 9, length);
					}
					SendRawAndRecycle(netPacket, request.RemoteEndPoint);
				}
				lock (_requestsDict)
				{
					_requestsDict.Remove(request.RemoteEndPoint);
				}
			}
			else
			{
				lock (_requestsDict)
				{
					if (!TryGetPeer(request.RemoteEndPoint, out actualValue))
					{
						if (request.Result == ConnectionRequestResult.Reject)
						{
							actualValue = new NetPeer(this, request.RemoteEndPoint, GetNextPeerId());
							actualValue.Reject(request.InternalPacket, rejectData, start, length);
							AddPeer(actualValue);
						}
						else
						{
							actualValue = new NetPeer(this, request, GetNextPeerId());
							AddPeer(actualValue);
							CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
						}
					}
					_requestsDict.Remove(request.RemoteEndPoint);
				}
			}
			return actualValue;
		}

		private int GetNextPeerId()
		{
			if (!_peerIds.TryDequeue(out var result))
			{
				return _lastPeerId++;
			}
			return result;
		}

		private void ProcessConnectRequest(IPEndPoint remoteEndPoint, NetPeer netPeer, NetConnectRequestPacket connRequest)
		{
			if (netPeer != null)
			{
				ConnectRequestResult connectRequestResult = netPeer.ProcessConnectRequest(connRequest);
				switch (connectRequestResult)
				{
				default:
					return;
				case ConnectRequestResult.Reconnection:
					DisconnectPeerForce(netPeer, DisconnectReason.Reconnect, SocketError.Success, null);
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				case ConnectRequestResult.NewConnection:
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				case ConnectRequestResult.P2PLose:
					DisconnectPeerForce(netPeer, DisconnectReason.PeerToPeerConnection, SocketError.Success, null);
					RemovePeer(netPeer, enableWriteLock: true);
					break;
				}
				if (connectRequestResult != ConnectRequestResult.P2PLose)
				{
					connRequest.ConnectionNumber = (byte)((netPeer.ConnectionNum + 1) % 4);
				}
			}
			ConnectionRequest value;
			lock (_requestsDict)
			{
				if (_requestsDict.TryGetValue(remoteEndPoint, out value))
				{
					value.UpdateRequest(connRequest);
					return;
				}
				value = new ConnectionRequest(remoteEndPoint, connRequest, this);
				_requestsDict.Add(remoteEndPoint, value);
			}
			CreateEvent(NetEvent.EType.ConnectionRequest, null, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, value, DeliveryMethod.Unreliable, 0);
		}

		private void OnMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			if (packet.Size == 0)
			{
				PoolRecycle(packet);
				return;
			}
			_dropPacket = false;
			if (!_dropPacket)
			{
				HandleMessageReceived(packet, remoteEndPoint);
			}
		}

		[Conditional("DEBUG")]
		private void HandleSimulateLatency(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			if (!SimulateLatency)
			{
				return;
			}
			int num = _randomGenerator.Next(SimulationMinLatency, SimulationMaxLatency);
			if (num > 5)
			{
				lock (_pingSimulationList)
				{
					_pingSimulationList.Add(new IncomingData
					{
						Data = packet,
						EndPoint = remoteEndPoint,
						TimeWhenGet = DateTime.UtcNow.AddMilliseconds(num)
					});
				}
				_dropPacket = true;
			}
		}

		[Conditional("DEBUG")]
		private void HandleSimulatePacketLoss()
		{
			if (SimulatePacketLoss && _randomGenerator.NextDouble() * 100.0 < (double)SimulationPacketLossChance)
			{
				_dropPacket = true;
			}
		}

		private void HandleMessageReceived(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			int size = packet.Size;
			if (EnableStatistics)
			{
				Statistics.IncrementPacketsReceived();
				Statistics.AddBytesReceived(size);
			}
			if (_ntpRequests.Count > 0 && _ntpRequests.TryGetValue(remoteEndPoint, out var _))
			{
				if (packet.Size >= 48)
				{
					byte[] array = new byte[packet.Size];
					Buffer.BlockCopy(packet.RawData, 0, array, 0, packet.Size);
					NtpPacket ntpPacket = NtpPacket.FromServerResponse(array, DateTime.UtcNow);
					try
					{
						ntpPacket.ValidateReply();
					}
					catch (InvalidOperationException)
					{
						ntpPacket = null;
					}
					if (ntpPacket != null)
					{
						_ntpRequests.TryRemove(remoteEndPoint, out var _);
						_ntpEventListener?.OnNtpResponse(ntpPacket);
					}
				}
				return;
			}
			if (_extraPacketLayer != null)
			{
				_extraPacketLayer.ProcessInboundPacket(ref remoteEndPoint, ref packet.RawData, ref packet.Size);
				if (packet.Size == 0)
				{
					return;
				}
			}
			if (!packet.Verify())
			{
				NetDebug.WriteError("[NM] DataReceived: bad!");
				PoolRecycle(packet);
				return;
			}
			switch (packet.Property)
			{
			case PacketProperty.ConnectRequest:
				if (NetConnectRequestPacket.GetProtocolId(packet) != 13)
				{
					SendRawAndRecycle(PoolGetWithProperty(PacketProperty.InvalidProtocol), remoteEndPoint);
					return;
				}
				break;
			case PacketProperty.Broadcast:
				if (BroadcastReceiveEnabled)
				{
					CreateEvent(NetEvent.EType.Broadcast, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet);
				}
				return;
			case PacketProperty.UnconnectedMessage:
				if (UnconnectedMessagesEnabled)
				{
					CreateEvent(NetEvent.EType.ReceiveUnconnected, null, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0, packet);
				}
				return;
			case PacketProperty.NatMessage:
				if (NatPunchEnabled)
				{
					NatPunchModule.ProcessMessage(remoteEndPoint, packet);
				}
				return;
			}
			NetPeer actualValue = remoteEndPoint as NetPeer;
			bool flag = actualValue != null || TryGetPeer(remoteEndPoint, out actualValue);
			if (flag && EnableStatistics)
			{
				actualValue.Statistics.IncrementPacketsReceived();
				actualValue.Statistics.AddBytesReceived(size);
			}
			switch (packet.Property)
			{
			case PacketProperty.ConnectRequest:
			{
				NetConnectRequestPacket netConnectRequestPacket = NetConnectRequestPacket.FromData(packet);
				if (netConnectRequestPacket != null)
				{
					ProcessConnectRequest(remoteEndPoint, actualValue, netConnectRequestPacket);
				}
				break;
			}
			case PacketProperty.PeerNotFound:
				if (flag)
				{
					if (actualValue.ConnectionState == ConnectionState.Connected)
					{
						if (packet.Size == 1)
						{
							actualValue.ResetMtu();
							SendRaw(NetConnectAcceptPacket.MakeNetworkChanged(actualValue), remoteEndPoint);
						}
						else if (packet.Size == 2 && packet.RawData[1] == 1)
						{
							DisconnectPeerForce(actualValue, DisconnectReason.PeerNotFound, SocketError.Success, null);
						}
					}
				}
				else
				{
					if (packet.Size <= 1)
					{
						break;
					}
					bool flag2 = false;
					if (AllowPeerAddressChange)
					{
						NetConnectAcceptPacket netConnectAcceptPacket = NetConnectAcceptPacket.FromData(packet);
						if (netConnectAcceptPacket != null && netConnectAcceptPacket.PeerNetworkChanged && netConnectAcceptPacket.PeerId < _peersArray.Length)
						{
							_peersLock.EnterUpgradeableReadLock();
							NetPeer netPeer = _peersArray[netConnectAcceptPacket.PeerId];
							_peersLock.ExitUpgradeableReadLock();
							if (netPeer != null && netPeer.ConnectTime == netConnectAcceptPacket.ConnectionTime && netPeer.ConnectionNum == netConnectAcceptPacket.ConnectionNumber)
							{
								if (netPeer.ConnectionState == ConnectionState.Connected)
								{
									netPeer.InitiateEndPointChange();
									CreateEvent(NetEvent.EType.PeerAddressChanged, netPeer, remoteEndPoint, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
								}
								flag2 = true;
							}
						}
					}
					PoolRecycle(packet);
					if (!flag2)
					{
						NetPacket netPacket = PoolGetWithProperty(PacketProperty.PeerNotFound, 1);
						netPacket.RawData[1] = 1;
						SendRawAndRecycle(netPacket, remoteEndPoint);
					}
				}
				break;
			case PacketProperty.InvalidProtocol:
				if (flag && actualValue.ConnectionState == ConnectionState.Outgoing)
				{
					DisconnectPeerForce(actualValue, DisconnectReason.InvalidProtocol, SocketError.Success, null);
				}
				break;
			case PacketProperty.Disconnect:
				if (flag)
				{
					DisconnectResult disconnectResult = actualValue.ProcessDisconnect(packet);
					if (disconnectResult == DisconnectResult.None)
					{
						PoolRecycle(packet);
						break;
					}
					DisconnectPeerForce(actualValue, (disconnectResult == DisconnectResult.Disconnect) ? DisconnectReason.RemoteConnectionClose : DisconnectReason.ConnectionRejected, SocketError.Success, packet);
				}
				else
				{
					PoolRecycle(packet);
				}
				SendRawAndRecycle(PoolGetWithProperty(PacketProperty.ShutdownOk), remoteEndPoint);
				break;
			case PacketProperty.ConnectAccept:
				if (flag)
				{
					NetConnectAcceptPacket netConnectAcceptPacket2 = NetConnectAcceptPacket.FromData(packet);
					if (netConnectAcceptPacket2 != null && actualValue.ProcessConnectAccept(netConnectAcceptPacket2))
					{
						CreateEvent(NetEvent.EType.Connect, actualValue, null, SocketError.Success, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					}
				}
				break;
			default:
				if (flag)
				{
					actualValue.ProcessPacket(packet);
				}
				else
				{
					SendRawAndRecycle(PoolGetWithProperty(PacketProperty.PeerNotFound), remoteEndPoint);
				}
				break;
			}
		}

		internal void CreateReceiveEvent(NetPacket packet, DeliveryMethod method, byte channelNumber, int headerSize, NetPeer fromPeer)
		{
			if (UnsyncedEvents || UnsyncedReceiveEvent || _manualMode)
			{
				NetEvent netEvent;
				lock (_eventLock)
				{
					netEvent = _netEventPoolHead;
					if (netEvent == null)
					{
						netEvent = new NetEvent(this);
					}
					else
					{
						_netEventPoolHead = netEvent.Next;
					}
				}
				netEvent.Next = null;
				netEvent.Type = NetEvent.EType.Receive;
				netEvent.DataReader.SetSource(packet, headerSize);
				netEvent.Peer = fromPeer;
				netEvent.DeliveryMethod = method;
				netEvent.ChannelNumber = channelNumber;
				ProcessEvent(netEvent);
				return;
			}
			lock (_eventLock)
			{
				NetEvent netEvent = _netEventPoolHead;
				if (netEvent == null)
				{
					netEvent = new NetEvent(this);
				}
				else
				{
					_netEventPoolHead = netEvent.Next;
				}
				netEvent.Next = null;
				netEvent.Type = NetEvent.EType.Receive;
				netEvent.DataReader.SetSource(packet, headerSize);
				netEvent.Peer = fromPeer;
				netEvent.DeliveryMethod = method;
				netEvent.ChannelNumber = channelNumber;
				if (_pendingEventTail == null)
				{
					_pendingEventHead = netEvent;
				}
				else
				{
					_pendingEventTail.Next = netEvent;
				}
				_pendingEventTail = netEvent;
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options)
		{
			SendToAll(writer.Data, 0, writer.Length, options);
		}

		public void SendToAll(byte[] data, DeliveryMethod options)
		{
			SendToAll(data, 0, data.Length, options);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options)
		{
			SendToAll(data, start, length, 0, options);
		}

		public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options)
		{
			SendToAll(writer.Data, 0, writer.Length, channelNumber, options);
		}

		public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options)
		{
			SendToAll(data, 0, data.Length, channelNumber, options);
		}

		public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options)
		{
			try
			{
				_peersLock.EnterReadLock();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					netPeer.Send(data, start, length, channelNumber, options);
				}
			}
			finally
			{
				_peersLock.ExitReadLock();
			}
		}

		public void SendToAll(NetDataWriter writer, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(writer.Data, 0, writer.Length, 0, options, excludePeer);
		}

		public void SendToAll(byte[] data, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, data.Length, 0, options, excludePeer);
		}

		public void SendToAll(byte[] data, int start, int length, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, start, length, 0, options, excludePeer);
		}

		public void SendToAll(NetDataWriter writer, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(writer.Data, 0, writer.Length, channelNumber, options, excludePeer);
		}

		public void SendToAll(byte[] data, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			SendToAll(data, 0, data.Length, channelNumber, options, excludePeer);
		}

		public void SendToAll(byte[] data, int start, int length, byte channelNumber, DeliveryMethod options, NetPeer excludePeer)
		{
			try
			{
				_peersLock.EnterReadLock();
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					if (netPeer != excludePeer)
					{
						netPeer.Send(data, start, length, channelNumber, options);
					}
				}
			}
			finally
			{
				_peersLock.ExitReadLock();
			}
		}

		public bool Start()
		{
			return Start(0);
		}

		public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port)
		{
			return Start(addressIPv4, addressIPv6, port, manualMode: false);
		}

		public bool Start(string addressIPv4, string addressIPv6, int port)
		{
			IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4);
			IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6);
			return Start(addressIPv7, addressIPv8, port);
		}

		public bool Start(int port)
		{
			return Start(IPAddress.Any, IPAddress.IPv6Any, port);
		}

		public bool StartInManualMode(IPAddress addressIPv4, IPAddress addressIPv6, int port)
		{
			return Start(addressIPv4, addressIPv6, port, manualMode: true);
		}

		public bool StartInManualMode(string addressIPv4, string addressIPv6, int port)
		{
			IPAddress addressIPv7 = NetUtils.ResolveAddress(addressIPv4);
			IPAddress addressIPv8 = NetUtils.ResolveAddress(addressIPv6);
			return StartInManualMode(addressIPv7, addressIPv8, port);
		}

		public bool StartInManualMode(int port)
		{
			return StartInManualMode(IPAddress.Any, IPAddress.IPv6Any, port);
		}

		public bool SendUnconnectedMessage(byte[] message, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(message, 0, message.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(NetDataWriter writer, string address, int port)
		{
			IPEndPoint remoteEndPoint = NetUtils.MakeEndPoint(address, port);
			return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(NetDataWriter writer, IPEndPoint remoteEndPoint)
		{
			return SendUnconnectedMessage(writer.Data, 0, writer.Length, remoteEndPoint);
		}

		public bool SendUnconnectedMessage(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			NetPacket packet = PoolGetWithData(PacketProperty.UnconnectedMessage, message, start, length);
			return SendRawAndRecycle(packet, remoteEndPoint) > 0;
		}

		public void TriggerUpdate()
		{
			_updateTriggerEvent.Set();
		}

		public void PollEvents(int maxProcessedEvents = 0)
		{
			if (_manualMode)
			{
				if (_udpSocketv4 != null)
				{
					ManualReceive(_udpSocketv4, _bufferEndPointv4, maxProcessedEvents);
				}
				if (_udpSocketv6 != null && _udpSocketv6 != _udpSocketv4)
				{
					ManualReceive(_udpSocketv6, _bufferEndPointv6, maxProcessedEvents);
				}
			}
			else
			{
				if (UnsyncedEvents)
				{
					return;
				}
				NetEvent netEvent;
				lock (_eventLock)
				{
					netEvent = _pendingEventHead;
					_pendingEventHead = null;
					_pendingEventTail = null;
				}
				int num = 0;
				while (netEvent != null)
				{
					NetEvent next = netEvent.Next;
					ProcessEvent(netEvent);
					netEvent = next;
					num++;
					if (num == maxProcessedEvents)
					{
						break;
					}
				}
			}
		}

		public NetPeer Connect(string address, int port, string key)
		{
			return Connect(address, port, NetDataWriter.FromString(key));
		}

		public NetPeer Connect(string address, int port, NetDataWriter connectionData)
		{
			IPEndPoint target;
			try
			{
				target = NetUtils.MakeEndPoint(address, port);
			}
			catch
			{
				CreateEvent(NetEvent.EType.Disconnect, null, null, SocketError.Success, 0, DisconnectReason.UnknownHost, null, DeliveryMethod.Unreliable, 0);
				return null;
			}
			return Connect(target, connectionData);
		}

		public NetPeer Connect(IPEndPoint target, string key)
		{
			return Connect(target, NetDataWriter.FromString(key));
		}

		public NetPeer Connect(IPEndPoint target, NetDataWriter connectionData)
		{
			if (!IsRunning)
			{
				throw new InvalidOperationException("Client is not running");
			}
			lock (_requestsDict)
			{
				if (_requestsDict.ContainsKey(target))
				{
					return null;
				}
				byte connectNum = 0;
				if (TryGetPeer(target, out var actualValue))
				{
					ConnectionState connectionState = actualValue.ConnectionState;
					if (connectionState == ConnectionState.Outgoing || connectionState == ConnectionState.Connected)
					{
						return actualValue;
					}
					connectNum = (byte)((actualValue.ConnectionNum + 1) % 4);
					RemovePeer(actualValue, enableWriteLock: true);
				}
				actualValue = new NetPeer(this, target, GetNextPeerId(), connectNum, connectionData);
				AddPeer(actualValue);
				return actualValue;
			}
		}

		public void Stop()
		{
			Stop(sendDisconnectMessages: true);
		}

		public void Stop(bool sendDisconnectMessages)
		{
			if (IsRunning)
			{
				for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
				{
					netPeer.Shutdown(null, 0, 0, !sendDisconnectMessages);
				}
				CloseSocket();
				_updateTriggerEvent.Set();
				if (!_manualMode)
				{
					_logicThread.Join();
					_logicThread = null;
				}
				ClearPeerSet();
				_peerIds = new ConcurrentQueue<int>();
				_lastPeerId = 0;
				_connectedPeersCount = 0L;
				_pendingEventHead = null;
				_pendingEventTail = null;
			}
		}

		[Conditional("DEBUG")]
		private void ClearPingSimulationList()
		{
			lock (_pingSimulationList)
			{
				_pingSimulationList.Clear();
			}
		}

		public int GetPeersCount(ConnectionState peerState)
		{
			int num = 0;
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					num++;
				}
			}
			_peersLock.ExitReadLock();
			return num;
		}

		public void GetPeersNonAlloc(List<NetPeer> peers, ConnectionState peerState)
		{
			peers.Clear();
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				if ((netPeer.ConnectionState & peerState) != 0)
				{
					peers.Add(netPeer);
				}
			}
			_peersLock.ExitReadLock();
		}

		public void DisconnectAll()
		{
			DisconnectAll(null, 0, 0);
		}

		public void DisconnectAll(byte[] data, int start, int count)
		{
			_peersLock.EnterReadLock();
			for (NetPeer netPeer = _headPeer; netPeer != null; netPeer = netPeer.NextPeer)
			{
				DisconnectPeer(netPeer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null);
			}
			_peersLock.ExitReadLock();
		}

		public void DisconnectPeerForce(NetPeer peer)
		{
			DisconnectPeerForce(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, null);
		}

		public void DisconnectPeer(NetPeer peer)
		{
			DisconnectPeer(peer, null, 0, 0);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data)
		{
			DisconnectPeer(peer, data, 0, data.Length);
		}

		public void DisconnectPeer(NetPeer peer, NetDataWriter writer)
		{
			DisconnectPeer(peer, writer.Data, 0, writer.Length);
		}

		public void DisconnectPeer(NetPeer peer, byte[] data, int start, int count)
		{
			DisconnectPeer(peer, DisconnectReason.DisconnectPeerCalled, SocketError.Success, force: false, data, start, count, null);
		}

		public void CreateNtpRequest(IPEndPoint endPoint)
		{
			_ntpRequests.TryAdd(endPoint, new NtpRequest(endPoint));
		}

		public void CreateNtpRequest(string ntpServerAddress, int port)
		{
			IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, port);
			_ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint));
		}

		public void CreateNtpRequest(string ntpServerAddress)
		{
			IPEndPoint iPEndPoint = NetUtils.MakeEndPoint(ntpServerAddress, 123);
			_ntpRequests.TryAdd(iPEndPoint, new NtpRequest(iPEndPoint));
		}

		public NetPeerEnumerator GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		IEnumerator<NetPeer> IEnumerable<NetPeer>.GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return new NetPeerEnumerator(_headPeer);
		}

		private static int HashSetGetPrime(int min)
		{
			int[] primes = Primes;
			foreach (int num in primes)
			{
				if (num >= min)
				{
					return num;
				}
			}
			for (int j = min | 1; j < int.MaxValue; j += 2)
			{
				if (IsPrime(j) && (j - 1) % 101 != 0)
				{
					return j;
				}
			}
			return min;
			static bool IsPrime(int candidate)
			{
				if (((uint)candidate & (true ? 1u : 0u)) != 0)
				{
					int num2 = (int)Math.Sqrt(candidate);
					for (int k = 3; k <= num2; k += 2)
					{
						if (candidate % k == 0)
						{
							return false;
						}
					}
					return true;
				}
				return candidate == 2;
			}
		}

		private void ClearPeerSet()
		{
			_peersLock.EnterWriteLock();
			_headPeer = null;
			if (_lastIndex > 0)
			{
				Array.Clear(_slots, 0, _lastIndex);
				Array.Clear(_buckets, 0, _buckets.Length);
				_lastIndex = 0;
				_count = 0;
				_freeList = -1;
			}
			_peersArray = new NetPeer[32];
			_peersLock.ExitWriteLock();
		}

		private bool ContainsPeer(NetPeer item)
		{
			if (item == null)
			{
				NetDebug.WriteError($"Contains peer null: {item}");
				return false;
			}
			if (_buckets != null)
			{
				int num = item.GetHashCode() & 0x7FFFFFFF;
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(item))
					{
						return true;
					}
				}
			}
			return false;
		}

		public NetPeer GetPeerById(int id)
		{
			if (id < 0 || id >= _peersArray.Length)
			{
				return null;
			}
			return _peersArray[id];
		}

		public bool TryGetPeerById(int id, out NetPeer peer)
		{
			peer = GetPeerById(id);
			return peer != null;
		}

		private void AddPeer(NetPeer peer)
		{
			if (peer == null)
			{
				NetDebug.WriteError($"Add peer null: {peer}");
				return;
			}
			_peersLock.EnterWriteLock();
			if (_headPeer != null)
			{
				peer.NextPeer = _headPeer;
				_headPeer.PrevPeer = peer;
			}
			_headPeer = peer;
			AddPeerToSet(peer);
			if (peer.Id >= _peersArray.Length)
			{
				int num = _peersArray.Length * 2;
				while (peer.Id >= num)
				{
					num *= 2;
				}
				Array.Resize(ref _peersArray, num);
			}
			_peersArray[peer.Id] = peer;
			_peersLock.ExitWriteLock();
		}

		private void RemovePeer(NetPeer peer, bool enableWriteLock)
		{
			if (enableWriteLock)
			{
				_peersLock.EnterWriteLock();
			}
			if (!RemovePeerFromSet(peer))
			{
				if (enableWriteLock)
				{
					_peersLock.ExitWriteLock();
				}
				return;
			}
			if (peer == _headPeer)
			{
				_headPeer = peer.NextPeer;
			}
			if (peer.PrevPeer != null)
			{
				peer.PrevPeer.NextPeer = peer.NextPeer;
			}
			if (peer.NextPeer != null)
			{
				peer.NextPeer.PrevPeer = peer.PrevPeer;
			}
			peer.PrevPeer = null;
			_peersArray[peer.Id] = null;
			_peerIds.Enqueue(peer.Id);
			if (enableWriteLock)
			{
				_peersLock.ExitWriteLock();
			}
		}

		private bool RemovePeerFromSet(NetPeer peer)
		{
			if (_buckets == null || peer == null)
			{
				return false;
			}
			int num = peer.GetHashCode() & 0x7FFFFFFF;
			int num2 = num % _buckets.Length;
			int num3 = -1;
			for (int num4 = _buckets[num2] - 1; num4 >= 0; num4 = _slots[num4].Next)
			{
				if (_slots[num4].HashCode == num && _slots[num4].Value.Equals(peer))
				{
					if (num3 < 0)
					{
						_buckets[num2] = _slots[num4].Next + 1;
					}
					else
					{
						_slots[num3].Next = _slots[num4].Next;
					}
					_slots[num4].HashCode = -1;
					_slots[num4].Value = null;
					_slots[num4].Next = _freeList;
					_count--;
					if (_count == 0)
					{
						_lastIndex = 0;
						_freeList = -1;
					}
					else
					{
						_freeList = num4;
					}
					return true;
				}
				num3 = num4;
			}
			return false;
		}

		private bool TryGetPeer(IPEndPoint endPoint, out NetPeer actualValue)
		{
			if (_buckets != null)
			{
				int num = endPoint.GetHashCode() & 0x7FFFFFFF;
				_peersLock.EnterReadLock();
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Equals(endPoint))
					{
						actualValue = _slots[num2].Value;
						_peersLock.ExitReadLock();
						return true;
					}
				}
				_peersLock.ExitReadLock();
			}
			actualValue = null;
			return false;
		}

		private bool TryGetPeer(SocketAddress saddr, out NetPeer actualValue)
		{
			if (_buckets != null)
			{
				int num = saddr.GetHashCode() & 0x7FFFFFFF;
				_peersLock.EnterReadLock();
				for (int num2 = _buckets[num % _buckets.Length] - 1; num2 >= 0; num2 = _slots[num2].Next)
				{
					if (_slots[num2].HashCode == num && _slots[num2].Value.Serialize().Equals(saddr))
					{
						actualValue = _slots[num2].Value;
						_peersLock.ExitReadLock();
						return true;
					}
				}
				_peersLock.ExitReadLock();
			}
			actualValue = null;
			return false;
		}

		private bool AddPeerToSet(NetPeer value)
		{
			if (_buckets == null)
			{
				int num = HashSetGetPrime(0);
				_buckets = new int[num];
				_slots = new Slot[num];
			}
			int num2 = value.GetHashCode() & 0x7FFFFFFF;
			int num3 = num2 % _buckets.Length;
			for (int num4 = _buckets[num2 % _buckets.Length] - 1; num4 >= 0; num4 = _slots[num4].Next)
			{
				if (_slots[num4].HashCode == num2 && _slots[num4].Value.Equals(value))
				{
					return false;
				}
			}
			int num5;
			if (_freeList >= 0)
			{
				num5 = _freeList;
				_freeList = _slots[num5].Next;
			}
			else
			{
				if (_lastIndex == _slots.Length)
				{
					int num6 = 2 * _count;
					num6 = (((uint)num6 > 2147483587u && 2147483587 > _count) ? 2147483587 : HashSetGetPrime(num6));
					Slot[] array = new Slot[num6];
					Array.Copy(_slots, 0, array, 0, _lastIndex);
					_buckets = new int[num6];
					for (int i = 0; i < _lastIndex; i++)
					{
						int num7 = array[i].HashCode % num6;
						array[i].Next = _buckets[num7] - 1;
						_buckets[num7] = i + 1;
					}
					_slots = array;
					num3 = num2 % _buckets.Length;
				}
				num5 = _lastIndex;
				_lastIndex++;
			}
			_slots[num5].HashCode = num2;
			_slots[num5].Value = value;
			_slots[num5].Next = _buckets[num3] - 1;
			_buckets[num3] = num5 + 1;
			_count++;
			return true;
		}

		private NetPacket PoolGetWithData(PacketProperty property, byte[] data, int start, int length)
		{
			int headerSize = NetPacket.GetHeaderSize(property);
			NetPacket netPacket = PoolGetPacket(length + headerSize);
			netPacket.Property = property;
			Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length);
			return netPacket;
		}

		private NetPacket PoolGetWithProperty(PacketProperty property, int size)
		{
			NetPacket netPacket = PoolGetPacket(size + NetPacket.GetHeaderSize(property));
			netPacket.Property = property;
			return netPacket;
		}

		private NetPacket PoolGetWithProperty(PacketProperty property)
		{
			NetPacket netPacket = PoolGetPacket(NetPacket.GetHeaderSize(property));
			netPacket.Property = property;
			return netPacket;
		}

		internal NetPacket PoolGetPacket(int size)
		{
			if (size > NetConstants.MaxPacketSize)
			{
				return new NetPacket(size);
			}
			NetPacket poolHead;
			lock (_poolLock)
			{
				poolHead = _poolHead;
				if (poolHead == null)
				{
					return new NetPacket(size);
				}
				_poolHead = _poolHead.Next;
				_poolCount--;
			}
			poolHead.Size = size;
			if (poolHead.RawData.Length < size)
			{
				poolHead.RawData = new byte[size];
			}
			return poolHead;
		}

		internal void PoolRecycle(NetPacket packet)
		{
			if (packet.RawData.Length > NetConstants.MaxPacketSize || _poolCount >= PacketPoolSize)
			{
				return;
			}
			packet.RawData[0] = 0;
			lock (_poolLock)
			{
				packet.Next = _poolHead;
				_poolHead = packet;
				_poolCount++;
			}
		}

		static NetManager()
		{
			Primes = new int[72]
			{
				3, 7, 11, 17, 23, 29, 37, 47, 59, 71,
				89, 107, 131, 163, 197, 239, 293, 353, 431, 521,
				631, 761, 919, 1103, 1327, 1597, 1931, 2333, 2801, 3371,
				4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591, 17519, 21023,
				25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363,
				156437, 187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403,
				968897, 1162687, 1395263, 1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559,
				5999471, 7199369
			};
			MulticastAddressV6 = IPAddress.Parse("ff02::1");
			IPv6Support = Socket.OSSupportsIPv6;
		}

		private bool ProcessError(SocketException ex)
		{
			switch (ex.SocketErrorCode)
			{
			case SocketError.NotConnected:
				NotConnected = true;
				return true;
			case SocketError.OperationAborted:
			case SocketError.Interrupted:
			case SocketError.NotSocket:
				return true;
			default:
				NetDebug.WriteError($"[R]Error code: {(int)ex.SocketErrorCode} - {ex}");
				CreateEvent(NetEvent.EType.Error, null, null, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
				break;
			case SocketError.WouldBlock:
			case SocketError.MessageSize:
			case SocketError.NetworkReset:
			case SocketError.ConnectionReset:
			case SocketError.TimedOut:
				break;
			}
			return false;
		}

		private void ManualReceive(Socket socket, EndPoint bufferEndPoint, int maxReceive)
		{
			try
			{
				int num = 0;
				while (socket.Available > 0)
				{
					ReceiveFrom(socket, ref bufferEndPoint);
					num++;
					if (num == maxReceive)
					{
						break;
					}
				}
			}
			catch (SocketException ex)
			{
				ProcessError(ex);
			}
			catch (ObjectDisposedException)
			{
			}
			catch (Exception ex3)
			{
				NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex3);
			}
		}

		private void NativeReceiveLogic()
		{
			IntPtr handle = _udpSocketv4.Handle;
			IntPtr s2 = _udpSocketv6?.Handle ?? IntPtr.Zero;
			byte[] address2 = new byte[16];
			byte[] address3 = new byte[28];
			IPEndPoint tempEndPoint = new IPEndPoint(IPAddress.Any, 0);
			List<Socket> list = new List<Socket>(2);
			Socket udpSocketv = _udpSocketv4;
			Socket udpSocketv2 = _udpSocketv6;
			NetPacket packet = PoolGetPacket(NetConstants.MaxPacketSize);
			while (IsRunning)
			{
				try
				{
					if (udpSocketv2 == null)
					{
						if (!NativeReceiveFrom(handle, address2))
						{
							break;
						}
						continue;
					}
					bool flag = false;
					if (udpSocketv.Available != 0 || list.Contains(udpSocketv))
					{
						if (!NativeReceiveFrom(handle, address2))
						{
							break;
						}
						flag = true;
					}
					if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2))
					{
						if (!NativeReceiveFrom(s2, address3))
						{
							break;
						}
						flag = true;
					}
					list.Clear();
					if (!flag)
					{
						list.Add(udpSocketv);
						list.Add(udpSocketv2);
						Socket.Select(list, null, null, 500000);
					}
				}
				catch (SocketException ex)
				{
					if (ProcessError(ex))
					{
						break;
					}
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (ThreadAbortException)
				{
					break;
				}
				catch (Exception ex4)
				{
					NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4);
				}
			}
			bool NativeReceiveFrom(IntPtr s, byte[] address)
			{
				int socketAddressSize = address.Length;
				packet.Size = NativeSocket.RecvFrom(s, packet.RawData, NetConstants.MaxPacketSize, address, ref socketAddressSize);
				if (packet.Size == 0)
				{
					return true;
				}
				if (packet.Size == -1)
				{
					return !ProcessError(new SocketException((int)NativeSocket.GetSocketError()));
				}
				short num = (short)((address[1] << 8) | address[0]);
				tempEndPoint.Port = (ushort)((address[2] << 8) | address[3]);
				if ((NativeSocket.UnixMode && num == 10) || (!NativeSocket.UnixMode && num == 23))
				{
					uint num2 = (uint)((address[27] << 24) + (address[26] << 16) + (address[25] << 8) + address[24]);
					byte[] array = new byte[16];
					Buffer.BlockCopy(address, 8, array, 0, 16);
					tempEndPoint.Address = new IPAddress(array, num2);
				}
				else
				{
					long newAddress = (uint)((address[4] & 0xFF) | ((address[5] << 8) & 0xFF00) | ((address[6] << 16) & 0xFF0000) | (address[7] << 24));
					tempEndPoint.Address = new IPAddress(newAddress);
				}
				if (TryGetPeer(tempEndPoint, out var actualValue))
				{
					OnMessageReceived(packet, actualValue);
				}
				else
				{
					OnMessageReceived(packet, tempEndPoint);
					tempEndPoint = new IPEndPoint(IPAddress.Any, 0);
				}
				packet = PoolGetPacket(NetConstants.MaxPacketSize);
				return true;
			}
		}

		private void ReceiveFrom(Socket s, ref EndPoint bufferEndPoint)
		{
			NetPacket netPacket = PoolGetPacket(NetConstants.MaxPacketSize);
			netPacket.Size = s.ReceiveFrom(netPacket.RawData, 0, NetConstants.MaxPacketSize, SocketFlags.None, ref bufferEndPoint);
			OnMessageReceived(netPacket, (IPEndPoint)bufferEndPoint);
		}

		private void ReceiveLogic()
		{
			EndPoint bufferEndPoint = new IPEndPoint(IPAddress.Any, 0);
			EndPoint bufferEndPoint2 = new IPEndPoint(IPAddress.IPv6Any, 0);
			List<Socket> list = new List<Socket>(2);
			Socket udpSocketv = _udpSocketv4;
			Socket udpSocketv2 = _udpSocketv6;
			while (IsRunning)
			{
				try
				{
					if (udpSocketv2 == null)
					{
						if (udpSocketv.Available != 0 || udpSocketv.Poll(500000, SelectMode.SelectRead))
						{
							ReceiveFrom(udpSocketv, ref bufferEndPoint);
						}
						continue;
					}
					bool flag = false;
					if (udpSocketv.Available != 0 || list.Contains(udpSocketv))
					{
						ReceiveFrom(udpSocketv, ref bufferEndPoint);
						flag = true;
					}
					if (udpSocketv2.Available != 0 || list.Contains(udpSocketv2))
					{
						ReceiveFrom(udpSocketv2, ref bufferEndPoint2);
						flag = true;
					}
					list.Clear();
					if (!flag)
					{
						list.Add(udpSocketv);
						list.Add(udpSocketv2);
						Socket.Select(list, null, null, 500000);
					}
				}
				catch (SocketException ex)
				{
					if (ProcessError(ex))
					{
						break;
					}
				}
				catch (ObjectDisposedException)
				{
					break;
				}
				catch (ThreadAbortException)
				{
					break;
				}
				catch (Exception ex4)
				{
					NetDebug.WriteError("[NM] SocketReceiveThread error: " + ex4);
				}
			}
		}

		public bool Start(IPAddress addressIPv4, IPAddress addressIPv6, int port, bool manualMode)
		{
			if (IsRunning && !NotConnected)
			{
				return false;
			}
			NotConnected = false;
			_manualMode = manualMode;
			UseNativeSockets = UseNativeSockets && NativeSocket.IsSupported;
			_udpSocketv4 = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			if (!BindSocket(_udpSocketv4, new IPEndPoint(addressIPv4, port)))
			{
				return false;
			}
			LocalPort = ((IPEndPoint)_udpSocketv4.LocalEndPoint).Port;
			IsRunning = true;
			if (_manualMode)
			{
				_bufferEndPointv4 = new IPEndPoint(IPAddress.Any, 0);
			}
			if (IPv6Support && IPv6Enabled)
			{
				_udpSocketv6 = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
				if (BindSocket(_udpSocketv6, new IPEndPoint(addressIPv6, LocalPort)))
				{
					if (_manualMode)
					{
						_bufferEndPointv6 = new IPEndPoint(IPAddress.IPv6Any, 0);
					}
				}
				else
				{
					_udpSocketv6 = null;
				}
			}
			if (!manualMode)
			{
				ThreadStart start = ReceiveLogic;
				if (UseNativeSockets)
				{
					start = NativeReceiveLogic;
				}
				_receiveThread = new Thread(start)
				{
					Name = $"ReceiveThread({LocalPort})",
					IsBackground = true
				};
				_receiveThread.Start();
				if (_logicThread == null)
				{
					_logicThread = new Thread(UpdateLogic)
					{
						Name = "LogicThread",
						IsBackground = true
					};
					_logicThread.Start();
				}
			}
			return true;
		}

		private bool BindSocket(Socket socket, IPEndPoint ep)
		{
			socket.ReceiveTimeout = 500;
			socket.SendTimeout = 500;
			socket.ReceiveBufferSize = 1048576;
			socket.SendBufferSize = 1048576;
			socket.Blocking = true;
			if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
			{
				try
				{
					socket.IOControl(-1744830452, new byte[1], null);
				}
				catch
				{
				}
			}
			try
			{
				socket.ExclusiveAddressUse = !ReuseAddress;
				socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, ReuseAddress);
				socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontRoute, DontRoute);
			}
			catch
			{
			}
			if (ep.AddressFamily == AddressFamily.InterNetwork)
			{
				Ttl = 255;
				try
				{
					socket.EnableBroadcast = true;
				}
				catch (SocketException ex)
				{
					NetDebug.WriteError($"[B]Broadcast error: {ex.SocketErrorCode}");
				}
				if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
				{
					try
					{
						socket.DontFragment = true;
					}
					catch (SocketException ex2)
					{
						NetDebug.WriteError($"[B]DontFragment error: {ex2.SocketErrorCode}");
					}
				}
			}
			try
			{
				socket.Bind(ep);
				if (ep.AddressFamily == AddressFamily.InterNetworkV6)
				{
					try
					{
						socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption(MulticastAddressV6));
					}
					catch (Exception)
					{
					}
				}
			}
			catch (SocketException ex4)
			{
				switch (ex4.SocketErrorCode)
				{
				case SocketError.AddressAlreadyInUse:
					if (socket.AddressFamily == AddressFamily.InterNetworkV6)
					{
						try
						{
							socket.DualMode = false;
							socket.Bind(ep);
						}
						catch (SocketException ex5)
						{
							NetDebug.WriteError($"[B]Bind exception: {ex5}, errorCode: {ex5.SocketErrorCode}");
							return false;
						}
						return true;
					}
					break;
				case SocketError.AddressFamilyNotSupported:
					return true;
				}
				NetDebug.WriteError($"[B]Bind exception: {ex4}, errorCode: {ex4.SocketErrorCode}");
				return false;
			}
			return true;
		}

		internal int SendRawAndRecycle(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			int result = SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
			PoolRecycle(packet);
			return result;
		}

		internal int SendRaw(NetPacket packet, IPEndPoint remoteEndPoint)
		{
			return SendRaw(packet.RawData, 0, packet.Size, remoteEndPoint);
		}

		internal unsafe int SendRaw(byte[] message, int start, int length, IPEndPoint remoteEndPoint)
		{
			if (!IsRunning)
			{
				return 0;
			}
			NetPacket netPacket = null;
			if (_extraPacketLayer != null)
			{
				netPacket = PoolGetPacket(length + _extraPacketLayer.ExtraPacketSizeForLayer);
				Buffer.BlockCopy(message, start, netPacket.RawData, 0, length);
				start = 0;
				_extraPacketLayer.ProcessOutBoundPacket(ref remoteEndPoint, ref netPacket.RawData, ref start, ref length);
				message = netPacket.RawData;
			}
			Socket socket = _udpSocketv4;
			if (remoteEndPoint.AddressFamily == AddressFamily.InterNetworkV6 && IPv6Support)
			{
				socket = _udpSocketv6;
				if (socket == null)
				{
					return 0;
				}
			}
			int num;
			try
			{
				if (UseNativeSockets && remoteEndPoint is NetPeer netPeer)
				{
					fixed (byte* pinnedBuffer = &message[start])
					{
						num = NativeSocket.SendTo(socket.Handle, pinnedBuffer, length, netPeer.NativeAddress, netPeer.NativeAddress.Length);
					}
					if (num == -1)
					{
						throw NativeSocket.GetSocketException();
					}
				}
				else
				{
					num = socket.SendTo(message, start, length, SocketFlags.None, remoteEndPoint);
				}
			}
			catch (SocketException ex)
			{
				switch (ex.SocketErrorCode)
				{
				case SocketError.Interrupted:
				case SocketError.NoBufferSpaceAvailable:
					return 0;
				case SocketError.MessageSize:
					return 0;
				case SocketError.NetworkUnreachable:
				case SocketError.HostUnreachable:
					if (DisconnectOnUnreachable && remoteEndPoint is NetPeer peer)
					{
						DisconnectPeerForce(peer, (ex.SocketErrorCode == SocketError.HostUnreachable) ? DisconnectReason.HostUnreachable : DisconnectReason.NetworkUnreachable, ex.SocketErrorCode, null);
					}
					CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					return -1;
				case SocketError.Shutdown:
					CreateEvent(NetEvent.EType.Error, null, remoteEndPoint, ex.SocketErrorCode, 0, DisconnectReason.ConnectionFailed, null, DeliveryMethod.Unreliable, 0);
					return -1;
				default:
					NetDebug.WriteError($"[S] {ex}");
					return -1;
				}
			}
			catch (Exception arg)
			{
				NetDebug.WriteError($"[S] {arg}");
				return 0;
			}
			finally
			{
				if (netPacket != null)
				{
					PoolRecycle(netPacket);
				}
			}
			if (num <= 0)
			{
				return 0;
			}
			if (EnableStatistics)
			{
				Statistics.IncrementPacketsSent();
				Statistics.AddBytesSent(length);
			}
			return num;
		}

		public bool SendBroadcast(NetDataWriter writer, int port)
		{
			return SendBroadcast(writer.Data, 0, writer.Length, port);
		}

		public bool SendBroadcast(byte[] data, int port)
		{
			return SendBroadcast(data, 0, data.Length, port);
		}

		public bool SendBroadcast(byte[] data, int start, int length, int port)
		{
			if (!IsRunning)
			{
				return false;
			}
			NetPacket netPacket;
			if (_extraPacketLayer != null)
			{
				int headerSize = NetPacket.GetHeaderSize(PacketProperty.Broadcast);
				netPacket = PoolGetPacket(headerSize + length + _extraPacketLayer.ExtraPacketSizeForLayer);
				netPacket.Property = PacketProperty.Broadcast;
				Buffer.BlockCopy(data, start, netPacket.RawData, headerSize, length);
				int offset = 0;
				int length2 = length + headerSize;
				IPEndPoint endPoint = null;
				_extraPacketLayer.ProcessOutBoundPacket(ref endPoint, ref netPacket.RawData, ref offset, ref length2);
			}
			else
			{
				netPacket = PoolGetWithData(PacketProperty.Broadcast, data, start, length);
			}
			bool flag = false;
			bool flag2 = false;
			try
			{
				flag = _udpSocketv4.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(IPAddress.Broadcast, port)) > 0;
				if (_udpSocketv6 != null)
				{
					flag2 = _udpSocketv6.SendTo(netPacket.RawData, 0, netPacket.Size, SocketFlags.None, new IPEndPoint(MulticastAddressV6, port)) > 0;
				}
			}
			catch (SocketException ex)
			{
				if (ex.SocketErrorCode == SocketError.HostUnreachable)
				{
					return flag;
				}
				NetDebug.WriteError($"[S][MCAST] {ex}");
				return flag;
			}
			catch (Exception arg)
			{
				NetDebug.WriteError($"[S][MCAST] {arg}");
				return flag;
			}
			finally
			{
				PoolRecycle(netPacket);
			}
			return flag || flag2;
		}

		private void CloseSocket()
		{
			IsRunning = false;
			_udpSocketv4?.Close();
			_udpSocketv6?.Close();
			_udpSocketv4 = null;
			_udpSocketv6 = null;
			if (_receiveThread != null && _receiveThread != Thread.CurrentThread)
			{
				_receiveThread.Join();
			}
			_receiveThread = null;
		}
	}
	internal enum PacketProperty : byte
	{
		Unreliable,
		Channeled,
		Ack,
		Ping,
		Pong,
		ConnectRequest,
		ConnectAccept,
		Disconnect,
		UnconnectedMessage,
		MtuCheck,
		MtuOk,
		Broadcast,
		Merged,
		ShutdownOk,
		PeerNotFound,
		InvalidProtocol,
		NatMessage,
		Empty
	}
	internal sealed class NetPacket
	{
		private static readonly int PropertiesCount;

		private static readonly int[] HeaderSizes;

		public byte[] RawData;

		public int Size;

		public object UserData;

		public NetPacket Next;

		public PacketProperty Property
		{
			get
			{
				return (PacketProperty)(RawData[0] & 0x1Fu);
			}
			set
			{
				RawData[0] = (byte)((RawData[0] & 0xE0u) | (uint)value);
			}
		}

		public byte ConnectionNumber
		{
			get
			{
				return (byte)((RawData[0] & 0x60) >> 5);
			}
			set
			{
				RawData[0] = (byte)((RawData[0] & 0x9Fu) | (uint)(value << 5));
			}
		}

		public ushort Sequence
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 1);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 1, value);
			}
		}

		public bool IsFragmented => (RawData[0] & 0x80) != 0;

		public byte ChannelId
		{
			get
			{
				return RawData[3];
			}
			set
			{
				RawData[3] = value;
			}
		}

		public ushort FragmentId
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 4);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 4, value);
			}
		}

		public ushort FragmentPart
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 6);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 6, value);
			}
		}

		public ushort FragmentsTotal
		{
			get
			{
				return BitConverter.ToUInt16(RawData, 8);
			}
			set
			{
				FastBitConverter.GetBytes(RawData, 8, value);
			}
		}

		static NetPacket()
		{
			PropertiesCount = Enum.GetValues(typeof(PacketProperty)).Length;
			HeaderSizes = NetUtils.AllocatePinnedUninitializedArray<int>(PropertiesCount);
			for (int i = 0; i < HeaderSizes.Length; i++)
			{
				switch ((PacketProperty)(byte)i)
				{
				case PacketProperty.Channeled:
				case PacketProperty.Ack:
					HeaderSizes[i] = 4;
					break;
				case PacketProperty.Ping:
					HeaderSizes[i] = 3;
					break;
				case PacketProperty.ConnectRequest:
					HeaderSizes[i] = 18;
					break;
				case PacketProperty.ConnectAccept:
					HeaderSizes[i] = 15;
					break;
				case PacketProperty.Disconnect:
					HeaderSizes[i] = 9;
					break;
				case PacketProperty.Pong:
					HeaderSizes[i] = 11;
					break;
				default:
					HeaderSizes[i] = 1;
					break;
				}
			}
		}

		public void MarkFragmented()
		{
			RawData[0] |= 128;
		}

		public NetPacket(int size)
		{
			RawData = new byte[size];
			Size = size;
		}

		public NetPacket(PacketProperty property, int size)
		{
			size += GetHeaderSize(property);
			RawData = new byte[size];
			Property = property;
			Size = size;
		}

		public static int GetHeaderSize(PacketProperty property)
		{
			return HeaderSizes[(uint)property];
		}

		public int GetHeaderSize()
		{
			return HeaderSizes[RawData[0] & 0x1F];
		}

		public bool Verify()
		{
			byte b = (byte)(RawData[0] & 0x1Fu);
			if (b >= PropertiesCount)
			{
				return false;
			}
			int num = HeaderSizes[b];
			bool flag = (RawData[0] & 0x80) != 0;
			if (Size >= num)
			{
				if (flag)
				{
					return Size >= num + 6;
				}
				return true;
			}
			return false;
		}
	}
	[Flags]
	public enum ConnectionState : byte
	{
		Outgoing = 2,
		Connected = 4,
		ShutdownRequested = 8,
		Disconnected = 0x10,
		EndPointChange = 0x20,
		Any = 0x2E
	}
	internal enum ConnectRequestResult
	{
		None,
		P2PLose,
		Reconnection,
		NewConnection
	}
	internal enum DisconnectResult
	{
		None,
		Reject,
		Disconnect
	}
	internal enum ShutdownResult
	{
		None,
		Success,
		WasConnected
	}
	public class NetPeer : IPEndPoint
	{
		private class IncomingFragments
		{
			public NetPacket[] Fragments;

			public int ReceivedCount;

			public int TotalSize;

			public byte ChannelId;
		}

		private int _rtt;

		private int _avgRtt;

		private int _rttCount;

		private double _resendDelay = 27.0;

		private float _pingSendTimer;

		private float _rttResetTimer;

		private readonly Stopwatch _pingTimer = new Stopwatch();

		private volatile float _timeSinceLastPacket;

		private long _remoteDelta;

		private readonly object _shutdownLock = new object();

		internal volatile NetPeer NextPeer;

		internal NetPeer PrevPeer;

		private NetPacket[] _unreliableSecondQueue;

		private NetPacket[] _unreliableChannel;

		private int _unreliablePendingCount;

		private readonly object _unreliableChannelLock = new object();

		private readonly ConcurrentQueue<BaseChannel> _channelSendQueue;

		private readonly BaseChannel[] _channels;

		private int _mtu;

		private int _mtuIdx;

		private bool _finishMtu;

		private float _mtuCheckTimer;

		private int _mtuCheckAttempts;

		private const int MtuCheckDelay = 1000;

		private const int MaxMtuCheckAttempts = 4;

		private readonly object _mtuMutex = new object();

		private int _fragmentId;

		private readonly Dictionary<ushort, IncomingFragments> _holdedFragments;

		private readonly Dictionary<ushort, ushort> _deliveredFragments;

		private readonly NetPacket _mergeData;

		private int _mergePos;

		private int _mergeCount;

		private int _connectAttempts;

		private float _connectTimer;

		private long _connectTime;

		private byte _connectNum;

		private ConnectionState _connectionState;

		private NetPacket _shutdownPacket;

		private const int ShutdownDelay = 300;

		private float _shutdownTimer;

		private readonly NetPacket _pingPacket;

		private readonly NetPacket _pongPacket;

		private readonly NetPacket _connectRequestPacket;

		private readonly NetPacket _connectAcceptPacket;

		public readonly NetManager NetManager;

		public readonly int Id;

		public object Tag;

		public readonly NetStatistics Statistics;

		private SocketAddress _cachedSocketAddr;

		private int _cachedHashCode;

		internal byte[] NativeAddress;

		internal byte ConnectionNum
		{
			get
			{
				return _connectNum;
			}
			private set
			{
				_connectNum = value;
				_mergeData.ConnectionNumber = value;
				_pingPacket.ConnectionNumber = value;
				_pongPacket.ConnectionNumber = value;
			}
		}

		public ConnectionState ConnectionState => _connectionState;

		internal long ConnectTime => _connectTime;

		public int RemoteId { get; private set; }

		public int Ping => _avgRtt / 2;

		public int RoundTripTime => _avgRtt;

		public int Mtu => _mtu;

		public long RemoteTimeDelta => _remoteDelta;

		public DateTime RemoteUtcTime => new DateTime(DateTime.UtcNow.Ticks + _remoteDelta);

		public float TimeSinceLastPacket => _timeSinceLastPacket;

		internal double ResendDelay => _resendDelay;

		public override SocketAddress Serialize()
		{
			return _cachedSocketAddr;
		}

		public override int GetHashCode()
		{
			return _cachedHashCode;
		}

		internal NetPeer(NetManager netManager, IPEndPoint remoteEndPoint, int id)
			: base(remoteEndPoint.Address, remoteEndPoint.Port)
		{
			Id = id;
			Statistics = new NetStatistics();
			NetManager = netManager;
			_cachedSocketAddr = base.Serialize();
			if (NetManager.UseNativeSockets)
			{
				NativeAddress = new byte[_cachedSocketAddr.Size];
				for (int i = 0; i < _cachedSocketAddr.Size; i++)
				{
					NativeAddress[i] = _cachedSocketAddr[i];
				}
			}
			_cachedHashCode = base.GetHashCode();
			ResetMtu();
			_connectionState = ConnectionState.Connected;
			_mergeData = new NetPacket(PacketProperty.Merged, NetConstants.MaxPacketSize);
			_pongPacket = new NetPacket(PacketProperty.Pong, 0);
			_pingPacket = new NetPacket(PacketProperty.Ping, 0)
			{
				Sequence = 1
			};
			_unreliableSecondQueue = new NetPacket[8];
			_unreliableChannel = new NetPacket[8];
			_holdedFragments = new Dictionary<ushort, IncomingFragments>();
			_deliveredFragments = new Dictionary<ushort, ushort>();
			_channels = new BaseChannel[netManager.ChannelsCount * 4];
			_channelSendQueue = new ConcurrentQueue<BaseChannel>();
		}

		internal void InitiateEndPointChange()
		{
			ResetMtu();
			_connectionState = ConnectionState.EndPointChange;
		}

		internal void FinishEndPointChange(IPEndPoint newEndPoint)
		{
			if (_connectionState != ConnectionState.EndPointChange)
			{
				return;
			}
			_connectionState = ConnectionState.Connected;
			base.Address = newEndPoint.Address;
			base.Port = newEndPoint.Port;
			if (NetManager.UseNativeSockets)
			{
				NativeAddress = new byte[_cachedSocketAddr.Size];
				for (int i = 0; i < _cachedSocketAddr.Size; i++)
				{
					NativeAddress[i] = _cachedSocketAddr[i];
				}
			}
			_cachedSocketAddr = base.Serialize();
			_cachedHashCode = base.GetHashCode();
		}

		internal void ResetMtu()
		{
			_finishMtu = !NetManager.MtuDiscovery;
			if (NetManager.MtuOverride > 0)
			{
				OverrideMtu(NetManager.MtuOverride);
			}
			else
			{
				SetMtu(0);
			}
		}

		private void SetMtu(int mtuIdx)
		{
			_mtuIdx = mtuIdx;
			_mtu = NetConstants.PossibleMtu[mtuIdx] - NetManager.ExtraPacketSizeForLayer;
		}

		private void OverrideMtu(int mtuValue)
		{
			_mtu = mtuValue;
			_finishMtu = true;
		}

		public int GetPacketsCountInReliableQueue(byte channelNumber, bool ordered)
		{
			int num = channelNumber * 4 + (ordered ? 2 : 0);
			BaseChannel baseChannel = _channels[num];
			if (baseChannel == null)
			{
				return 0;
			}
			return ((ReliableChannel)baseChannel).PacketsInQueue;
		}

		public PooledPacket CreatePacketFromPool(DeliveryMethod deliveryMethod, byte channelNumber)
		{
			int mtu = _mtu;
			NetPacket netPacket = NetManager.PoolGetPacket(mtu);
			if (deliveryMethod == DeliveryMethod.Unreliable)
			{
				netPacket.Property = PacketProperty.Unreliable;
				return new PooledPacket(netPacket, mtu, 0);
			}
			netPacket.Property = PacketProperty.Channeled;
			return new PooledPacket(netPacket, mtu, (byte)((uint)(channelNumber * 4) + (uint)deliveryMethod));
		}

		public void SendPooledPacket(PooledPacket packet, int userDataSize)
		{
			if (_connectionState == ConnectionState.Connected)
			{
				packet._packet.Size = packet.UserDataOffset + userDataSize;
				if (packet._packet.Property == PacketProperty.Channeled)
				{
					CreateChannel(packet._channelNumber).AddToQueue(packet._packet);
				}
				else
				{
					EnqueueUnreliable(packet._packet);
				}
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void EnqueueUnreliable(NetPacket packet)
		{
			lock (_unreliableChannelLock)
			{
				if (_unreliablePendingCount == _unreliableChannel.Length)
				{
					Array.Resize(ref _unreliableChannel, _unreliablePendingCount * 2);
				}
				_unreliableChannel[_unreliablePendingCount++] = packet;
			}
		}

		private BaseChannel CreateChannel(byte idx)
		{
			BaseChannel baseChannel = _channels[idx];
			if (baseChannel != null)
			{
				return baseChannel;
			}
			switch ((DeliveryMethod)(byte)(idx % 4))
			{
			case DeliveryMethod.ReliableUnordered:
				baseChannel = new ReliableChannel(this, ordered: false, idx);
				break;
			case DeliveryMethod.Sequenced:
				baseChannel = new SequencedChannel(this, reliable: false, idx);
				break;
			case DeliveryMethod.ReliableOrdered:
				baseChannel = new ReliableChannel(this, ordered: true, idx);
				break;
			case DeliveryMethod.ReliableSequenced:
				baseChannel = new SequencedChannel(this, reliable: true, idx);
				break;
			}
			Ba