Decompiled source of TechtonicaDirectConnect v1.0.20

plugins/TechtonicaDirectConnect.dll

Decompiled 6 hours ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mirror;
using UnityEngine;
using UnityEngine.SceneManagement;
using kcp2k;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace TechtonicaDirectConnect
{
	[BepInPlugin("com.certifried.techtonicadirectconnect", "Techtonica Direct Connect", "1.0.20")]
	[BepInProcess("Techtonica.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private static Plugin _instance;

		private Harmony _harmony;

		private bool _showConnectUI;

		private string _serverAddress = "";

		private string _serverPort = "6968";

		private string _statusMessage = "";

		private bool _isConnecting;

		private GUIStyle _windowStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _textFieldStyle;

		private GUIStyle _buttonStyle;

		private GUIStyle _statusStyle;

		private Rect _windowRect = new Rect((float)(Screen.width / 2 - 200), (float)(Screen.height / 2 - 100), 400f, 200f);

		private static KcpTransport _kcpTransport;

		private static Transport _originalTransport;

		private static bool _isDirectConnectActive;

		private const int VK_F11 = 122;

		private const int VK_ESCAPE = 27;

		private bool _f11WasPressed;

		private bool _escWasPressed;

		private float _debugTimer;

		private bool _updateRunning;

		private int _lastToggleFrame = -1;

		public static ManualLogSource Log { get; private set; }

		public static ConfigEntry<int> DefaultPort { get; private set; }

		public static ConfigEntry<string> LastServerAddress { get; private set; }

		public static ConfigEntry<KeyCode> ConnectHotkey { get; private set; }

		public static Plugin Instance => _instance;

		[DllImport("user32.dll")]
		private static extern short GetAsyncKeyState(int vKey);

		private void Awake()
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			_instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			DefaultPort = ((BaseUnityPlugin)this).Config.Bind<int>("General", "DefaultPort", 6968, "Default server port");
			LastServerAddress = ((BaseUnityPlugin)this).Config.Bind<string>("General", "LastServerAddress", "", "Last connected server address");
			ConnectHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ConnectHotkey", (KeyCode)292, "Hotkey to open connect dialog");
			_serverAddress = LastServerAddress.Value;
			_serverPort = DefaultPort.Value.ToString();
			_harmony = new Harmony("com.certifried.techtonicadirectconnect");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			NullSafetyPatches.ApplyPatches(_harmony);
			Log.LogInfo((object)"[Techtonica Direct Connect] v1.0.20 loaded!");
			Log.LogInfo((object)"[Techtonica Direct Connect] Press F11 to open connect dialog");
		}

		private void Update()
		{
			if (!_updateRunning)
			{
				_updateRunning = true;
				Log.LogInfo((object)"[DirectConnect] Update() is running!");
			}
			bool keyDown = Input.GetKeyDown((KeyCode)292);
			bool keyDown2 = Input.GetKeyDown((KeyCode)27);
			bool flag = (GetAsyncKeyState(122) & 0x8000) != 0;
			bool flag2 = (GetAsyncKeyState(27) & 0x8000) != 0;
			_debugTimer += Time.deltaTime;
			if (_debugTimer > 5f)
			{
				_debugTimer = 0f;
				Log.LogInfo((object)$"[DirectConnect] F11: Unity={keyDown}, Win={flag}, UI={_showConnectUI}");
			}
			if ((keyDown || (flag && !_f11WasPressed)) && Time.frameCount != _lastToggleFrame)
			{
				_lastToggleFrame = Time.frameCount;
				_showConnectUI = !_showConnectUI;
				if (_showConnectUI)
				{
					_statusMessage = "";
				}
				Log.LogInfo((object)$"[DirectConnect] UI toggled via Update: {_showConnectUI}");
			}
			_f11WasPressed = flag;
			bool flag3 = keyDown2 || (flag2 && !_escWasPressed);
			if (_showConnectUI && flag3)
			{
				_showConnectUI = false;
				Log.LogInfo((object)"[DirectConnect] UI closed via ESC");
			}
			_escWasPressed = flag2;
		}

		private void OnGUI()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			Event current = Event.current;
			if (current != null && (int)current.type == 4 && (int)current.keyCode == 292)
			{
				if (Time.frameCount != _lastToggleFrame)
				{
					_lastToggleFrame = Time.frameCount;
					_showConnectUI = !_showConnectUI;
					if (_showConnectUI)
					{
						_statusMessage = "";
					}
					Log.LogInfo((object)$"[DirectConnect] UI toggled via Event.current: {_showConnectUI}");
				}
				current.Use();
			}
			if (_showConnectUI)
			{
				InitStyles();
				_windowRect = GUI.Window(12345, _windowRect, new WindowFunction(DrawConnectWindow), "Direct Connect", _windowStyle);
			}
		}

		private void InitStyles()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Expected O, but got Unknown
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: 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_022b: Expected O, but got Unknown
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Expected O, but got Unknown
			if (_windowStyle == null)
			{
				_windowStyle = new GUIStyle(GUI.skin.window);
				_windowStyle.normal.background = MakeTexture(2, 2, new Color(0.1f, 0.1f, 0.15f, 0.95f));
				_windowStyle.fontSize = 16;
				_windowStyle.fontStyle = (FontStyle)1;
				_windowStyle.normal.textColor = new Color(0.65f, 0.55f, 0.98f);
				_windowStyle.padding = new RectOffset(15, 15, 25, 15);
				_labelStyle = new GUIStyle(GUI.skin.label);
				_labelStyle.fontSize = 14;
				_labelStyle.normal.textColor = Color.white;
				_textFieldStyle = new GUIStyle(GUI.skin.textField);
				_textFieldStyle.fontSize = 14;
				_textFieldStyle.normal.background = MakeTexture(2, 2, new Color(0.15f, 0.15f, 0.2f, 1f));
				_textFieldStyle.normal.textColor = Color.white;
				_textFieldStyle.padding = new RectOffset(10, 10, 8, 8);
				_buttonStyle = new GUIStyle(GUI.skin.button);
				_buttonStyle.fontSize = 14;
				_buttonStyle.fontStyle = (FontStyle)1;
				_buttonStyle.normal.background = MakeTexture(2, 2, new Color(0.49f, 0.23f, 0.93f, 1f));
				_buttonStyle.hover.background = MakeTexture(2, 2, new Color(0.59f, 0.33f, 1f, 1f));
				_buttonStyle.normal.textColor = Color.white;
				_buttonStyle.hover.textColor = Color.white;
				_buttonStyle.padding = new RectOffset(15, 15, 10, 10);
				_statusStyle = new GUIStyle(GUI.skin.label);
				_statusStyle.fontSize = 12;
				_statusStyle.alignment = (TextAnchor)4;
				_statusStyle.wordWrap = true;
			}
		}

		private Texture2D MakeTexture(int width, int height, Color color)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			Color[] array = (Color[])(object)new Color[width * height];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = color;
			}
			Texture2D val = new Texture2D(width, height);
			val.SetPixels(array);
			val.Apply();
			return val;
		}

		private void DrawConnectWindow(int windowID)
		{
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Expected O, but got Unknown
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Server:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			_serverAddress = GUILayout.TextField(_serverAddress, _textFieldStyle, Array.Empty<GUILayoutOption>());
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Label("Port:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) });
			_serverPort = GUILayout.TextField(_serverPort, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) });
			GUILayout.EndHorizontal();
			GUILayout.Space(15f);
			if (NetworkClient.active)
			{
				_statusStyle.normal.textColor = new Color(0.2f, 0.83f, 0.6f);
				GUILayout.Label("Connected to " + NetworkManager.singleton?.networkAddress, _statusStyle, Array.Empty<GUILayoutOption>());
				GUILayout.Space(10f);
				if (GUILayout.Button("Disconnect", _buttonStyle, Array.Empty<GUILayoutOption>()))
				{
					Disconnect();
				}
			}
			else
			{
				if (!string.IsNullOrEmpty(_statusMessage))
				{
					_statusStyle.normal.textColor = ((_statusMessage.Contains("Error") || _statusMessage.Contains("Failed")) ? new Color(0.97f, 0.44f, 0.44f) : new Color(0.65f, 0.55f, 0.98f));
					GUILayout.Label(_statusMessage, _statusStyle, Array.Empty<GUILayoutOption>());
					GUILayout.Space(5f);
				}
				GUI.enabled = !_isConnecting && !string.IsNullOrWhiteSpace(_serverAddress);
				if (GUILayout.Button(_isConnecting ? "Connecting..." : "Connect", _buttonStyle, Array.Empty<GUILayoutOption>()))
				{
					Connect();
				}
				GUI.enabled = true;
			}
			GUILayout.Space(10f);
			GUIStyle val = new GUIStyle(_buttonStyle);
			val.normal.background = MakeTexture(2, 2, new Color(0.3f, 0.3f, 0.35f, 1f));
			if (GUILayout.Button("Close", val, Array.Empty<GUILayoutOption>()))
			{
				_showConnectUI = false;
			}
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 30f));
		}

		private void Connect()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrWhiteSpace(_serverAddress))
			{
				_statusMessage = "Please enter a server address";
				return;
			}
			if (!int.TryParse(_serverPort, out var result) || result < 1 || result > 65535)
			{
				_statusMessage = "Invalid port number";
				return;
			}
			try
			{
				Scene val = SceneManager.GetActiveScene();
				string name = ((Scene)(ref val)).name;
				Log.LogInfo((object)("[DirectConnect] Current scene: " + name));
				for (int i = 0; i < SceneManager.sceneCount; i++)
				{
					Scene sceneAt = SceneManager.GetSceneAt(i);
					Log.LogInfo((object)$"[DirectConnect] Loaded scene {i}: {((Scene)(ref sceneAt)).name} (active: {sceneAt == SceneManager.GetActiveScene()})");
				}
				bool flag = false;
				for (int j = 0; j < SceneManager.sceneCount; j++)
				{
					val = SceneManager.GetSceneAt(j);
					string name2 = ((Scene)(ref val)).name;
					if (name2.Contains("Player") || name2.Contains("Voxeland"))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					_statusMessage = "Load a game first, then press F11";
					Log.LogWarning((object)"[DirectConnect] Not in game world - please start/load a game before connecting");
					return;
				}
				_isConnecting = true;
				_statusMessage = "Connecting...";
				LastServerAddress.Value = _serverAddress;
				if (!EnableDirectConnect(result))
				{
					_statusMessage = "Failed to initialize connection";
					_isConnecting = false;
					return;
				}
				NetworkManager singleton = NetworkManager.singleton;
				if ((Object)(object)singleton == (Object)null)
				{
					_statusMessage = "Error: NetworkManager not found";
					_isConnecting = false;
					return;
				}
				singleton.networkAddress = _serverAddress;
				singleton.StartClient();
				Log.LogInfo((object)$"[DirectConnect] Connecting to {_serverAddress}:{result}...");
				((MonoBehaviour)this).StartCoroutine(CheckConnection());
			}
			catch (Exception ex)
			{
				_statusMessage = "Error: " + ex.Message;
				_isConnecting = false;
				Log.LogError((object)$"[DirectConnect] Connection error: {ex}");
			}
		}

		private IEnumerator CheckConnection()
		{
			float timeout = 15f;
			float elapsed = 0f;
			while (elapsed < timeout)
			{
				if (NetworkClient.isConnected)
				{
					Log.LogInfo((object)"[DirectConnect] Connected! Sending ready signal...");
					_statusMessage = "Connected! Joining game...";
					if (!NetworkClient.ready)
					{
						try
						{
							NetworkClient.Ready();
							Log.LogInfo((object)"[DirectConnect] Sent Ready signal");
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("[DirectConnect] Ready failed: " + ex.Message));
						}
					}
					yield return (object)new WaitForSeconds(0.5f);
					try
					{
						if (NetworkClient.ready && (Object)(object)NetworkClient.localPlayer == (Object)null)
						{
							NetworkClient.AddPlayer();
							Log.LogInfo((object)"[DirectConnect] Requested player spawn");
						}
					}
					catch (Exception ex2)
					{
						Log.LogWarning((object)("[DirectConnect] AddPlayer failed: " + ex2.Message));
					}
					_statusMessage = "Connected!";
					_isConnecting = false;
					yield return (object)new WaitForSeconds(1f);
					_showConnectUI = false;
					yield break;
				}
				if (!NetworkClient.active && !_isConnecting)
				{
					_statusMessage = "Connection failed";
					yield break;
				}
				elapsed += 0.5f;
				yield return (object)new WaitForSeconds(0.5f);
			}
			if (!NetworkClient.isConnected)
			{
				_statusMessage = "Connection timed out";
				_isConnecting = false;
				NetworkManager singleton = NetworkManager.singleton;
				if (singleton != null)
				{
					singleton.StopClient();
				}
			}
		}

		private void Disconnect()
		{
			try
			{
				NetworkManager singleton = NetworkManager.singleton;
				if (singleton != null)
				{
					singleton.StopClient();
				}
				DisableDirectConnect();
				_statusMessage = "Disconnected";
				Log.LogInfo((object)"[DirectConnect] Disconnected");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[DirectConnect] Disconnect error: {arg}");
			}
		}

		private static bool EnableDirectConnect(int port)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			try
			{
				NetworkManager singleton = NetworkManager.singleton;
				if ((Object)(object)singleton == (Object)null)
				{
					Log.LogError((object)"[DirectConnect] NetworkManager not found!");
					return false;
				}
				FieldInfo field = typeof(NetworkManager).GetField("transport", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					Log.LogError((object)"[DirectConnect] Could not find transport field!");
					return false;
				}
				object? value = field.GetValue(singleton);
				_originalTransport = (Transport)((value is Transport) ? value : null);
				if ((Object)(object)_kcpTransport == (Object)null)
				{
					GameObject val = new GameObject("DirectConnect_KcpTransport");
					Object.DontDestroyOnLoad((Object)val);
					_kcpTransport = val.AddComponent<KcpTransport>();
				}
				_kcpTransport.Port = (ushort)port;
				_kcpTransport.NoDelay = true;
				_kcpTransport.Interval = 10u;
				_kcpTransport.Timeout = 10000;
				field.SetValue(singleton, _kcpTransport);
				typeof(Transport).GetField("activeTransport", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(null, _kcpTransport);
				_isDirectConnectActive = true;
				Log.LogInfo((object)$"[DirectConnect] Switched to KCP transport on port {port}");
				return true;
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"[DirectConnect] Failed to enable: {arg}");
				return false;
			}
		}

		private static void DisableDirectConnect()
		{
			if ((Object)(object)_originalTransport != (Object)null && (Object)(object)NetworkManager.singleton != (Object)null)
			{
				try
				{
					typeof(NetworkManager).GetField("transport", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(NetworkManager.singleton, _originalTransport);
					typeof(Transport).GetField("activeTransport", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(null, _originalTransport);
					_isDirectConnectActive = false;
					Log.LogInfo((object)"[DirectConnect] Restored original transport");
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"[DirectConnect] Failed to restore transport: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)_kcpTransport != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_kcpTransport).gameObject);
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.certifried.techtonicadirectconnect";

		public const string PLUGIN_NAME = "Techtonica Direct Connect";

		public const string PLUGIN_VERSION = "1.0.20";
	}
	public static class NullSafetyPatches
	{
		private static bool _patchesApplied;

		public static void ApplyPatches(Harmony harmony)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Expected O, but got Unknown
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Expected O, but got Unknown
			if (_patchesApplied)
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("NetworkedPlayer");
				if (type != null)
				{
					HarmonyMethod val = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null);
					MethodInfo methodInfo = AccessTools.Method(type, "Update", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkedPlayer.Update to skip");
					}
					MethodInfo methodInfo2 = AccessTools.Method(type, "OnStartClient", (Type[])null, (Type[])null);
					if (methodInfo2 != null)
					{
						harmony.Patch((MethodBase)methodInfo2, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkedPlayer.OnStartClient to skip");
					}
					MethodInfo methodInfo3 = AccessTools.Method(type, "OnStartLocalPlayer", (Type[])null, (Type[])null);
					if (methodInfo3 != null)
					{
						harmony.Patch((MethodBase)methodInfo3, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkedPlayer.OnStartLocalPlayer to skip");
					}
				}
				else
				{
					Plugin.Log.LogWarning((object)"[DirectConnect] NetworkedPlayer type not found");
				}
				Type type2 = AccessTools.TypeByName("ThirdPersonDisplayAnimator");
				if (type2 != null)
				{
					MethodInfo methodInfo4 = AccessTools.Method(type2, "Update", (Type[])null, (Type[])null);
					if (methodInfo4 != null)
					{
						HarmonyMethod val2 = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo4, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched ThirdPersonDisplayAnimator.Update to skip");
					}
					MethodInfo methodInfo5 = AccessTools.Method(type2, "UpdateSillyStuff", (Type[])null, (Type[])null);
					if (methodInfo5 != null)
					{
						HarmonyMethod val3 = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo5, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched ThirdPersonDisplayAnimator.UpdateSillyStuff to skip");
					}
				}
				else
				{
					Plugin.Log.LogWarning((object)"[DirectConnect] ThirdPersonDisplayAnimator type not found");
				}
				Type type3 = AccessTools.TypeByName("NetworkMessageRelay");
				if (type3 != null)
				{
					MethodInfo methodInfo6 = AccessTools.Method(type3, "SendNetworkAction", (Type[])null, (Type[])null);
					if (methodInfo6 != null)
					{
						HarmonyMethod val4 = new HarmonyMethod(typeof(NullSafetyPatches), "SuppressException_Finalizer", (Type[])null);
						harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val4, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.SendNetworkAction with finalizer");
					}
				}
				_patchesApplied = true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[DirectConnect] Failed to apply null safety patches: {arg}");
			}
		}

		public static bool Skip_Prefix()
		{
			return false;
		}

		public static Exception SuppressException_Finalizer(Exception __exception)
		{
			if (__exception is NullReferenceException)
			{
				return null;
			}
			return __exception;
		}
	}
}

plugins/kcp2k.dll

Decompiled 6 hours ago
using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using WhereAllocation;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("kcp2k.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[CompilerGenerated]
[EditorBrowsable(EditorBrowsableState.Never)]
[GeneratedCode("Unity.MonoScriptGenerator.MonoScriptInfoGenerator", null)]
internal class UnitySourceGeneratedAssemblyMonoScriptTypes_v1
{
	private struct MonoScriptData
	{
		public byte[] FilePathsData;

		public byte[] TypesData;

		public int TotalTypes;

		public int TotalFiles;

		public bool IsEditorOnly;
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	private static MonoScriptData Get()
	{
		MonoScriptData result = default(MonoScriptData);
		result.FilePathsData = new byte[1087]
		{
			0, 0, 0, 1, 0, 0, 0, 65, 92, 65,
			115, 115, 101, 116, 115, 92, 77, 105, 114, 114,
			111, 114, 92, 82, 117, 110, 116, 105, 109, 101,
			92, 84, 114, 97, 110, 115, 112, 111, 114, 116,
			92, 75, 67, 80, 92, 107, 99, 112, 50, 107,
			92, 104, 105, 103, 104, 108, 101, 118, 101, 108,
			92, 75, 99, 112, 67, 108, 105, 101, 110, 116,
			46, 99, 115, 0, 0, 0, 1, 0, 0, 0,
			75, 92, 65, 115, 115, 101, 116, 115, 92, 77,
			105, 114, 114, 111, 114, 92, 82, 117, 110, 116,
			105, 109, 101, 92, 84, 114, 97, 110, 115, 112,
			111, 114, 116, 92, 75, 67, 80, 92, 107, 99,
			112, 50, 107, 92, 104, 105, 103, 104, 108, 101,
			118, 101, 108, 92, 75, 99, 112, 67, 108, 105,
			101, 110, 116, 67, 111, 110, 110, 101, 99, 116,
			105, 111, 110, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 69, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 75, 99, 112,
			67, 111, 110, 110, 101, 99, 116, 105, 111, 110,
			46, 99, 115, 0, 0, 0, 1, 0, 0, 0,
			65, 92, 65, 115, 115, 101, 116, 115, 92, 77,
			105, 114, 114, 111, 114, 92, 82, 117, 110, 116,
			105, 109, 101, 92, 84, 114, 97, 110, 115, 112,
			111, 114, 116, 92, 75, 67, 80, 92, 107, 99,
			112, 50, 107, 92, 104, 105, 103, 104, 108, 101,
			118, 101, 108, 92, 75, 99, 112, 83, 101, 114,
			118, 101, 114, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 75, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 75, 99, 112,
			83, 101, 114, 118, 101, 114, 67, 111, 110, 110,
			101, 99, 116, 105, 111, 110, 46, 99, 115, 0,
			0, 0, 1, 0, 0, 0, 59, 92, 65, 115,
			115, 101, 116, 115, 92, 77, 105, 114, 114, 111,
			114, 92, 82, 117, 110, 116, 105, 109, 101, 92,
			84, 114, 97, 110, 115, 112, 111, 114, 116, 92,
			75, 67, 80, 92, 107, 99, 112, 50, 107, 92,
			104, 105, 103, 104, 108, 101, 118, 101, 108, 92,
			76, 111, 103, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 92, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
			65, 108, 108, 111, 99, 92, 75, 99, 112, 67,
			108, 105, 101, 110, 116, 67, 111, 110, 110, 101,
			99, 116, 105, 111, 110, 78, 111, 110, 65, 108,
			108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 82, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
			65, 108, 108, 111, 99, 92, 75, 99, 112, 67,
			108, 105, 101, 110, 116, 78, 111, 110, 65, 108,
			108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 92, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
			65, 108, 108, 111, 99, 92, 75, 99, 112, 83,
			101, 114, 118, 101, 114, 67, 111, 110, 110, 101,
			99, 116, 105, 111, 110, 78, 111, 110, 65, 108,
			108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
			0, 0, 0, 82, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
			104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
			65, 108, 108, 111, 99, 92, 75, 99, 112, 83,
			101, 114, 118, 101, 114, 78, 111, 110, 65, 108,
			108, 111, 99, 46, 99, 115, 0, 0, 0, 2,
			0, 0, 0, 53, 92, 65, 115, 115, 101, 116,
			115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
			117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
			110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
			92, 107, 99, 112, 50, 107, 92, 107, 99, 112,
			92, 75, 99, 112, 46, 99, 115, 0, 0, 0,
			1, 0, 0, 0, 54, 92, 65, 115, 115, 101,
			116, 115, 92, 77, 105, 114, 114, 111, 114, 92,
			82, 117, 110, 116, 105, 109, 101, 92, 84, 114,
			97, 110, 115, 112, 111, 114, 116, 92, 75, 67,
			80, 92, 107, 99, 112, 50, 107, 92, 107, 99,
			112, 92, 80, 111, 111, 108, 46, 99, 115, 0,
			0, 0, 1, 0, 0, 0, 57, 92, 65, 115,
			115, 101, 116, 115, 92, 77, 105, 114, 114, 111,
			114, 92, 82, 117, 110, 116, 105, 109, 101, 92,
			84, 114, 97, 110, 115, 112, 111, 114, 116, 92,
			75, 67, 80, 92, 107, 99, 112, 50, 107, 92,
			107, 99, 112, 92, 83, 101, 103, 109, 101, 110,
			116, 46, 99, 115, 0, 0, 0, 1, 0, 0,
			0, 55, 92, 65, 115, 115, 101, 116, 115, 92,
			77, 105, 114, 114, 111, 114, 92, 82, 117, 110,
			116, 105, 109, 101, 92, 84, 114, 97, 110, 115,
			112, 111, 114, 116, 92, 75, 67, 80, 92, 107,
			99, 112, 50, 107, 92, 107, 99, 112, 92, 85,
			116, 105, 108, 115, 46, 99, 115
		};
		result.TypesData = new byte[355]
		{
			0, 0, 0, 0, 15, 107, 99, 112, 50, 107,
			124, 75, 99, 112, 67, 108, 105, 101, 110, 116,
			0, 0, 0, 0, 25, 107, 99, 112, 50, 107,
			124, 75, 99, 112, 67, 108, 105, 101, 110, 116,
			67, 111, 110, 110, 101, 99, 116, 105, 111, 110,
			0, 0, 0, 0, 19, 107, 99, 112, 50, 107,
			124, 75, 99, 112, 67, 111, 110, 110, 101, 99,
			116, 105, 111, 110, 0, 0, 0, 0, 15, 107,
			99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
			114, 118, 101, 114, 0, 0, 0, 0, 25, 107,
			99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
			114, 118, 101, 114, 67, 111, 110, 110, 101, 99,
			116, 105, 111, 110, 0, 0, 0, 0, 9, 107,
			99, 112, 50, 107, 124, 76, 111, 103, 0, 0,
			0, 0, 33, 107, 99, 112, 50, 107, 124, 75,
			99, 112, 67, 108, 105, 101, 110, 116, 67, 111,
			110, 110, 101, 99, 116, 105, 111, 110, 78, 111,
			110, 65, 108, 108, 111, 99, 0, 0, 0, 0,
			23, 107, 99, 112, 50, 107, 124, 75, 99, 112,
			67, 108, 105, 101, 110, 116, 78, 111, 110, 65,
			108, 108, 111, 99, 0, 0, 0, 0, 33, 107,
			99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
			114, 118, 101, 114, 67, 111, 110, 110, 101, 99,
			116, 105, 111, 110, 78, 111, 110, 65, 108, 108,
			111, 99, 0, 0, 0, 0, 23, 107, 99, 112,
			50, 107, 124, 75, 99, 112, 83, 101, 114, 118,
			101, 114, 78, 111, 110, 65, 108, 108, 111, 99,
			0, 0, 0, 0, 9, 107, 99, 112, 50, 107,
			124, 75, 99, 112, 0, 0, 0, 0, 17, 107,
			99, 112, 50, 107, 46, 75, 99, 112, 124, 65,
			99, 107, 73, 116, 101, 109, 0, 0, 0, 0,
			10, 107, 99, 112, 50, 107, 124, 80, 111, 111,
			108, 0, 0, 0, 0, 13, 107, 99, 112, 50,
			107, 124, 83, 101, 103, 109, 101, 110, 116, 0,
			0, 0, 0, 11, 107, 99, 112, 50, 107, 124,
			85, 116, 105, 108, 115
		};
		result.TotalFiles = 14;
		result.TotalTypes = 15;
		result.IsEditorOnly = false;
		return result;
	}
}
namespace kcp2k;

public enum KcpChannel : byte
{
	Reliable = 1,
	Unreliable
}
public class KcpClient
{
	public Action OnConnected;

	public Action<ArraySegment<byte>> OnData;

	public Action OnDisconnected;

	public KcpClientConnection connection;

	public bool connected;

	public KcpClient(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
	{
		this.OnConnected = OnConnected;
		this.OnData = OnData;
		this.OnDisconnected = OnDisconnected;
	}

	protected virtual KcpClientConnection CreateConnection()
	{
		return new KcpClientConnection();
	}

	public void Connect(string address, ushort port, bool noDelay, uint interval, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
	{
		if (connected)
		{
			Log.Warning("KCP: client already connected!");
			return;
		}
		connection = CreateConnection();
		connection.OnAuthenticated = delegate
		{
			Log.Info("KCP: OnClientConnected");
			connected = true;
			OnConnected();
		};
		connection.OnData = delegate(ArraySegment<byte> message)
		{
			OnData(message);
		};
		connection.OnDisconnected = delegate
		{
			Log.Info("KCP: OnClientDisconnected");
			connected = false;
			connection = null;
			OnDisconnected();
		};
		connection.Connect(address, port, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
	}

	public void Send(ArraySegment<byte> segment, KcpChannel channel)
	{
		if (connected)
		{
			connection.SendData(segment, channel);
		}
		else
		{
			Log.Warning("KCP: can't send because client not connected!");
		}
	}

	public void Disconnect()
	{
		if (connected)
		{
			connection?.Disconnect();
		}
	}

	public void TickIncoming()
	{
		connection?.RawReceive();
		connection?.TickIncoming();
	}

	public void TickOutgoing()
	{
		connection?.TickOutgoing();
	}

	public void Tick()
	{
		TickIncoming();
		TickOutgoing();
	}

	public void Pause()
	{
		connection?.Pause();
	}

	public void Unpause()
	{
		connection?.Unpause();
	}
}
public class KcpClientConnection : KcpConnection
{
	private readonly byte[] rawReceiveBuffer = new byte[1200];

	public static bool ResolveHostname(string hostname, out IPAddress[] addresses)
	{
		try
		{
			addresses = Dns.GetHostAddresses(hostname);
			return addresses.Length >= 1;
		}
		catch (SocketException)
		{
			Log.Info("Failed to resolve host: " + hostname);
			addresses = null;
			return false;
		}
	}

	protected virtual void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
	{
		remoteEndPoint = new IPEndPoint(addresses[0], port);
	}

	protected virtual int ReceiveFrom(byte[] buffer)
	{
		return socket.ReceiveFrom(buffer, ref remoteEndPoint);
	}

	public void Connect(string host, ushort port, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
	{
		Log.Info($"KcpClient: connect to {host}:{port}");
		if (ResolveHostname(host, out var addresses))
		{
			CreateRemoteEndPoint(addresses, port);
			socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
			socket.Connect(remoteEndPoint);
			SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
			SendHandshake();
			RawReceive();
		}
		else
		{
			OnDisconnected();
		}
	}

	public void RawReceive()
	{
		try
		{
			if (socket == null)
			{
				return;
			}
			while (socket.Poll(0, SelectMode.SelectRead))
			{
				int num = ReceiveFrom(rawReceiveBuffer);
				if (num <= rawReceiveBuffer.Length)
				{
					RawInput(rawReceiveBuffer, num);
					continue;
				}
				Log.Error($"KCP ClientConnection: message of size {num} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting.");
				Disconnect();
			}
		}
		catch (SocketException)
		{
		}
	}

	protected override void Dispose()
	{
		socket.Close();
		socket = null;
	}

	protected override void RawSend(byte[] data, int length)
	{
		socket.Send(data, length, SocketFlags.None);
	}
}
internal enum KcpState
{
	Connected,
	Authenticated,
	Disconnected
}
public abstract class KcpConnection
{
	protected Socket socket;

	protected EndPoint remoteEndPoint;

	internal Kcp kcp;

	private KcpState state = KcpState.Disconnected;

	public Action OnAuthenticated;

	public Action<ArraySegment<byte>> OnData;

	public Action OnDisconnected;

	private bool paused;

	public const int DEFAULT_TIMEOUT = 10000;

	public int timeout = 10000;

	private uint lastReceiveTime;

	private readonly Stopwatch refTime = new Stopwatch();

	private const int CHANNEL_HEADER_SIZE = 1;

	public const int ReliableMaxMessageSize = 149224;

	public const int UnreliableMaxMessageSize = 1199;

	private byte[] kcpMessageBuffer = new byte[149225];

	private byte[] kcpSendBuffer = new byte[149225];

	private byte[] rawSendBuffer = new byte[1200];

	public const int PING_INTERVAL = 1000;

	private uint lastPingTime;

	internal const int QueueDisconnectThreshold = 10000;

	public int SendQueueCount => kcp.snd_queue.Count;

	public int ReceiveQueueCount => kcp.rcv_queue.Count;

	public int SendBufferCount => kcp.snd_buf.Count;

	public int ReceiveBufferCount => kcp.rcv_buf.Count;

	public uint MaxSendRate => kcp.snd_wnd * kcp.mtu * 1000 / kcp.interval;

	public uint MaxReceiveRate => kcp.rcv_wnd * kcp.mtu * 1000 / kcp.interval;

	protected void SetupKcp(bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
	{
		kcp = new Kcp(0u, RawSendReliable);
		kcp.SetNoDelay(noDelay ? 1u : 0u, interval, fastResend, !congestionWindow);
		kcp.SetWindowSize(sendWindowSize, receiveWindowSize);
		kcp.SetMtu(1199u);
		this.timeout = timeout;
		state = KcpState.Connected;
		refTime.Start();
	}

	private void HandleTimeout(uint time)
	{
		if (time >= lastReceiveTime + timeout)
		{
			Log.Warning($"KCP: Connection timed out after not receiving any message for {timeout}ms. Disconnecting.");
			Disconnect();
		}
	}

	private void HandleDeadLink()
	{
		if (kcp.state == -1)
		{
			Log.Warning("KCP Connection dead_link detected. Disconnecting.");
			Disconnect();
		}
	}

	private void HandlePing(uint time)
	{
		if (time >= lastPingTime + 1000)
		{
			SendPing();
			lastPingTime = time;
		}
	}

	private void HandleChoked()
	{
		int num = kcp.rcv_queue.Count + kcp.snd_queue.Count + kcp.rcv_buf.Count + kcp.snd_buf.Count;
		if (num >= 10000)
		{
			Log.Warning("KCP: disconnecting connection because it can't process data fast enough.\n" + $"Queue total {num}>{10000}. rcv_queue={kcp.rcv_queue.Count} snd_queue={kcp.snd_queue.Count} rcv_buf={kcp.rcv_buf.Count} snd_buf={kcp.snd_buf.Count}\n" + "* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n* Or perhaps the network is simply too slow on our end, or on the other end.\n");
			kcp.snd_queue.Clear();
			Disconnect();
		}
	}

	private bool ReceiveNextReliable(out KcpHeader header, out ArraySegment<byte> message)
	{
		int num = kcp.PeekSize();
		if (num > 0)
		{
			if (num <= kcpMessageBuffer.Length)
			{
				int num2 = kcp.Receive(kcpMessageBuffer, num);
				if (num2 >= 0)
				{
					header = (KcpHeader)kcpMessageBuffer[0];
					message = new ArraySegment<byte>(kcpMessageBuffer, 1, num - 1);
					lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
					return true;
				}
				Log.Warning($"Receive failed with error={num2}. closing connection.");
				Disconnect();
			}
			else
			{
				Log.Warning($"KCP: possible allocation attack for msgSize {num} > buffer {kcpMessageBuffer.Length}. Disconnecting the connection.");
				Disconnect();
			}
		}
		message = default(ArraySegment<byte>);
		header = KcpHeader.Disconnect;
		return false;
	}

	private void TickIncoming_Connected(uint time)
	{
		HandleTimeout(time);
		HandleDeadLink();
		HandlePing(time);
		HandleChoked();
		if (ReceiveNextReliable(out var header, out var _))
		{
			switch (header)
			{
			case KcpHeader.Handshake:
				Log.Info("KCP: received handshake");
				state = KcpState.Authenticated;
				OnAuthenticated?.Invoke();
				break;
			case KcpHeader.Data:
			case KcpHeader.Disconnect:
				Log.Warning($"KCP: received invalid header {header} while Connected. Disconnecting the connection.");
				Disconnect();
				break;
			case KcpHeader.Ping:
				break;
			}
		}
	}

	private void TickIncoming_Authenticated(uint time)
	{
		HandleTimeout(time);
		HandleDeadLink();
		HandlePing(time);
		HandleChoked();
		KcpHeader header;
		ArraySegment<byte> message;
		while (!paused && ReceiveNextReliable(out header, out message))
		{
			switch (header)
			{
			case KcpHeader.Handshake:
				Log.Warning($"KCP: received invalid header {header} while Authenticated. Disconnecting the connection.");
				Disconnect();
				break;
			case KcpHeader.Data:
				if (message.Count > 0)
				{
					OnData?.Invoke(message);
					break;
				}
				Log.Warning("KCP: received empty Data message while Authenticated. Disconnecting the connection.");
				Disconnect();
				break;
			case KcpHeader.Disconnect:
				Log.Info("KCP: received disconnect message");
				Disconnect();
				break;
			}
		}
	}

	public void TickIncoming()
	{
		uint time = (uint)refTime.ElapsedMilliseconds;
		try
		{
			switch (state)
			{
			case KcpState.Connected:
				TickIncoming_Connected(time);
				break;
			case KcpState.Authenticated:
				TickIncoming_Authenticated(time);
				break;
			case KcpState.Disconnected:
				break;
			}
		}
		catch (SocketException arg)
		{
			Log.Info($"KCP Connection: Disconnecting because {arg}. This is fine.");
			Disconnect();
		}
		catch (ObjectDisposedException arg2)
		{
			Log.Info($"KCP Connection: Disconnecting because {arg2}. This is fine.");
			Disconnect();
		}
		catch (Exception ex)
		{
			Log.Error(ex.ToString());
			Disconnect();
		}
	}

	public void TickOutgoing()
	{
		uint currentTimeMilliSeconds = (uint)refTime.ElapsedMilliseconds;
		try
		{
			switch (state)
			{
			case KcpState.Connected:
			case KcpState.Authenticated:
				kcp.Update(currentTimeMilliSeconds);
				break;
			}
		}
		catch (SocketException arg)
		{
			Log.Info($"KCP Connection: Disconnecting because {arg}. This is fine.");
			Disconnect();
		}
		catch (ObjectDisposedException arg2)
		{
			Log.Info($"KCP Connection: Disconnecting because {arg2}. This is fine.");
			Disconnect();
		}
		catch (Exception ex)
		{
			Log.Error(ex.ToString());
			Disconnect();
		}
	}

	public void RawInput(byte[] buffer, int msgLength)
	{
		if (msgLength <= 0)
		{
			return;
		}
		byte b = buffer[0];
		switch (b)
		{
		case 1:
		{
			int num = kcp.Input(buffer, 1, msgLength - 1);
			if (num != 0)
			{
				Log.Warning($"Input failed with error={num} for buffer with length={msgLength - 1}");
			}
			break;
		}
		case 2:
			if (state == KcpState.Authenticated)
			{
				if (!paused)
				{
					ArraySegment<byte> obj = new ArraySegment<byte>(buffer, 1, msgLength - 1);
					OnData?.Invoke(obj);
				}
				lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
			}
			else
			{
				Log.Warning($"KCP: received unreliable message in state {state}. Disconnecting the connection.");
				Disconnect();
			}
			break;
		default:
			Log.Info($"Disconnecting connection because of invalid channel header: {b}");
			Disconnect();
			break;
		}
	}

	protected abstract void RawSend(byte[] data, int length);

	private void RawSendReliable(byte[] data, int length)
	{
		rawSendBuffer[0] = 1;
		Buffer.BlockCopy(data, 0, rawSendBuffer, 1, length);
		RawSend(rawSendBuffer, length + 1);
	}

	private void SendReliable(KcpHeader header, ArraySegment<byte> content)
	{
		if (1 + content.Count <= kcpSendBuffer.Length)
		{
			kcpSendBuffer[0] = (byte)header;
			if (content.Count > 0)
			{
				Buffer.BlockCopy(content.Array, content.Offset, kcpSendBuffer, 1, content.Count);
			}
			int num = kcp.Send(kcpSendBuffer, 0, 1 + content.Count);
			if (num < 0)
			{
				Log.Warning($"Send failed with error={num} for content with length={content.Count}");
			}
		}
		else
		{
			Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={149224}");
		}
	}

	private void SendUnreliable(ArraySegment<byte> message)
	{
		if (message.Count <= 1199)
		{
			rawSendBuffer[0] = 2;
			Buffer.BlockCopy(message.Array, 0, rawSendBuffer, 1, message.Count);
			RawSend(rawSendBuffer, message.Count + 1);
		}
		else
		{
			Log.Error($"Failed to send unreliable message of size {message.Count} because it's larger than UnreliableMaxMessageSize={1199}");
		}
	}

	public void SendHandshake()
	{
		Log.Info("KcpConnection: sending Handshake to other end!");
		SendReliable(KcpHeader.Handshake, default(ArraySegment<byte>));
	}

	public void SendData(ArraySegment<byte> data, KcpChannel channel)
	{
		if (data.Count == 0)
		{
			Log.Warning("KcpConnection: tried sending empty message. This should never happen. Disconnecting.");
			Disconnect();
			return;
		}
		switch (channel)
		{
		case KcpChannel.Reliable:
			SendReliable(KcpHeader.Data, data);
			break;
		case KcpChannel.Unreliable:
			SendUnreliable(data);
			break;
		}
	}

	private void SendPing()
	{
		SendReliable(KcpHeader.Ping, default(ArraySegment<byte>));
	}

	private void SendDisconnect()
	{
		SendReliable(KcpHeader.Disconnect, default(ArraySegment<byte>));
	}

	protected virtual void Dispose()
	{
	}

	public void Disconnect()
	{
		if (state == KcpState.Disconnected)
		{
			return;
		}
		if (socket.Connected)
		{
			try
			{
				SendDisconnect();
				kcp.Flush();
			}
			catch (SocketException)
			{
			}
			catch (ObjectDisposedException)
			{
			}
		}
		Log.Info("KCP Connection: Disconnected.");
		state = KcpState.Disconnected;
		OnDisconnected?.Invoke();
	}

	public EndPoint GetRemoteEndPoint()
	{
		return remoteEndPoint;
	}

	public void Pause()
	{
		paused = true;
	}

	public void Unpause()
	{
		paused = false;
		lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
	}
}
public enum KcpHeader : byte
{
	Handshake = 1,
	Ping,
	Data,
	Disconnect
}
public class KcpServer
{
	public Action<int> OnConnected;

	public Action<int, ArraySegment<byte>> OnData;

	public Action<int> OnDisconnected;

	public bool DualMode;

	public bool NoDelay;

	public uint Interval;

	public int FastResend;

	public bool CongestionWindow;

	public uint SendWindowSize;

	public uint ReceiveWindowSize;

	public int Timeout;

	protected Socket socket;

	private EndPoint newClientEP;

	private readonly byte[] rawReceiveBuffer = new byte[1200];

	public Dictionary<int, KcpServerConnection> connections = new Dictionary<int, KcpServerConnection>();

	private HashSet<int> connectionsToRemove = new HashSet<int>();

	public KcpServer(Action<int> OnConnected, Action<int, ArraySegment<byte>> OnData, Action<int> OnDisconnected, bool DualMode, bool NoDelay, uint Interval, int FastResend = 0, bool CongestionWindow = true, uint SendWindowSize = 32u, uint ReceiveWindowSize = 128u, int Timeout = 10000)
	{
		this.OnConnected = OnConnected;
		this.OnData = OnData;
		this.OnDisconnected = OnDisconnected;
		this.DualMode = DualMode;
		this.NoDelay = NoDelay;
		this.Interval = Interval;
		this.FastResend = FastResend;
		this.CongestionWindow = CongestionWindow;
		this.SendWindowSize = SendWindowSize;
		this.ReceiveWindowSize = ReceiveWindowSize;
		this.Timeout = Timeout;
		newClientEP = (DualMode ? new IPEndPoint(IPAddress.IPv6Any, 0) : new IPEndPoint(IPAddress.Any, 0));
	}

	public bool IsActive()
	{
		return socket != null;
	}

	public void Start(ushort port)
	{
		if (socket != null)
		{
			Log.Warning("KCP: server already started!");
		}
		if (DualMode)
		{
			socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
			socket.DualMode = true;
			socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
		}
		else
		{
			socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
			socket.Bind(new IPEndPoint(IPAddress.Any, port));
		}
	}

	public void Send(int connectionId, ArraySegment<byte> segment, KcpChannel channel)
	{
		if (connections.TryGetValue(connectionId, out var value))
		{
			value.SendData(segment, channel);
		}
	}

	public void Disconnect(int connectionId)
	{
		if (connections.TryGetValue(connectionId, out var value))
		{
			value.Disconnect();
		}
	}

	public string GetClientAddress(int connectionId)
	{
		if (connections.TryGetValue(connectionId, out var value))
		{
			return (value.GetRemoteEndPoint() as IPEndPoint).Address.ToString();
		}
		return "";
	}

	protected virtual int ReceiveFrom(byte[] buffer, out int connectionHash)
	{
		int result = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP);
		connectionHash = newClientEP.GetHashCode();
		return result;
	}

	protected virtual KcpServerConnection CreateConnection()
	{
		return new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
	}

	public void TickIncoming()
	{
		while (socket != null && socket.Poll(0, SelectMode.SelectRead))
		{
			try
			{
				int connectionId;
				int num = ReceiveFrom(rawReceiveBuffer, out connectionId);
				if (num <= rawReceiveBuffer.Length)
				{
					if (!connections.TryGetValue(connectionId, out var connection))
					{
						connection = CreateConnection();
						connection.OnAuthenticated = delegate
						{
							connection.SendHandshake();
							connections.Add(connectionId, connection);
							Log.Info($"KCP: server added connection({connectionId})");
							connection.OnData = delegate(ArraySegment<byte> message)
							{
								OnData(connectionId, message);
							};
							connection.OnDisconnected = delegate
							{
								connectionsToRemove.Add(connectionId);
								Log.Info($"KCP: OnServerDisconnected({connectionId})");
								OnDisconnected(connectionId);
							};
							Log.Info($"KCP: OnServerConnected({connectionId})");
							OnConnected(connectionId);
						};
						connection.RawInput(rawReceiveBuffer, num);
						connection.TickIncoming();
					}
					else
					{
						connection.RawInput(rawReceiveBuffer, num);
					}
				}
				else
				{
					Log.Error($"KCP Server: message of size {num} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting connectionId={connectionId}.");
					Disconnect(connectionId);
				}
			}
			catch (SocketException)
			{
			}
		}
		foreach (KcpServerConnection value in connections.Values)
		{
			value.TickIncoming();
		}
		foreach (int item in connectionsToRemove)
		{
			connections.Remove(item);
		}
		connectionsToRemove.Clear();
	}

	public void TickOutgoing()
	{
		foreach (KcpServerConnection value in connections.Values)
		{
			value.TickOutgoing();
		}
	}

	public void Tick()
	{
		TickIncoming();
		TickOutgoing();
	}

	public void Stop()
	{
		socket?.Close();
		socket = null;
	}

	public void Pause()
	{
		foreach (KcpServerConnection value in connections.Values)
		{
			value.Pause();
		}
	}

	public void Unpause()
	{
		foreach (KcpServerConnection value in connections.Values)
		{
			value.Unpause();
		}
	}
}
public class KcpServerConnection : KcpConnection
{
	public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
	{
		base.socket = socket;
		base.remoteEndPoint = remoteEndPoint;
		SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
	}

	protected override void RawSend(byte[] data, int length)
	{
		socket.SendTo(data, 0, length, SocketFlags.None, remoteEndPoint);
	}
}
public static class Log
{
	public static Action<string> Info = Console.WriteLine;

	public static Action<string> Warning = Console.WriteLine;

	public static Action<string> Error = Console.Error.WriteLine;
}
public class KcpClientConnectionNonAlloc : KcpClientConnection
{
	private IPEndPointNonAlloc reusableEP;

	protected override void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000f: Expected O, but got Unknown
		reusableEP = new IPEndPointNonAlloc(addresses[0], (int)port);
		base.CreateRemoteEndPoint(addresses, port);
	}

	protected override int ReceiveFrom(byte[] buffer)
	{
		return Extensions.ReceiveFrom_NonAlloc(socket, buffer, reusableEP);
	}
}
public class KcpClientNonAlloc : KcpClient
{
	public KcpClientNonAlloc(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
		: base(OnConnected, OnData, OnDisconnected)
	{
	}

	protected override KcpClientConnection CreateConnection()
	{
		return new KcpClientConnectionNonAlloc();
	}
}
public class KcpServerConnectionNonAlloc : KcpServerConnection
{
	private IPEndPointNonAlloc reusableSendEndPoint;

	public KcpServerConnectionNonAlloc(Socket socket, EndPoint remoteEndpoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
		: base(socket, remoteEndpoint, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout)
	{
		this.reusableSendEndPoint = reusableSendEndPoint;
	}

	protected override void RawSend(byte[] data, int length)
	{
		Extensions.SendTo_NonAlloc(socket, data, 0, length, SocketFlags.None, reusableSendEndPoint);
	}
}
public class KcpServerNonAlloc : KcpServer
{
	private IPEndPointNonAlloc reusableClientEP;

	public KcpServerNonAlloc(Action<int> OnConnected, Action<int, ArraySegment<byte>> OnData, Action<int> OnDisconnected, bool DualMode, bool NoDelay, uint Interval, int FastResend = 0, bool CongestionWindow = true, uint SendWindowSize = 32u, uint ReceiveWindowSize = 128u, int Timeout = 10000)
		: base(OnConnected, OnData, OnDisconnected, DualMode, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Expected O, but got Unknown
		reusableClientEP = (DualMode ? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0) : new IPEndPointNonAlloc(IPAddress.Any, 0));
	}

	protected override int ReceiveFrom(byte[] buffer, out int connectionHash)
	{
		int result = Extensions.ReceiveFrom_NonAlloc(socket, buffer, 0, buffer.Length, SocketFlags.None, reusableClientEP);
		SocketAddress temp = reusableClientEP.temp;
		connectionHash = temp.GetHashCode();
		return result;
	}

	protected override KcpServerConnection CreateConnection()
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		IPEndPoint iPEndPoint = reusableClientEP.DeepCopyIPEndPoint();
		IPEndPointNonAlloc reusableSendEndPoint = new IPEndPointNonAlloc(iPEndPoint.Address, iPEndPoint.Port);
		return new KcpServerConnectionNonAlloc(socket, iPEndPoint, reusableSendEndPoint, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
	}
}
public class Kcp
{
	internal struct AckItem
	{
		internal uint serialNumber;

		internal uint timestamp;
	}

	public const int RTO_NDL = 30;

	public const int RTO_MIN = 100;

	public const int RTO_DEF = 200;

	public const int RTO_MAX = 60000;

	public const int CMD_PUSH = 81;

	public const int CMD_ACK = 82;

	public const int CMD_WASK = 83;

	public const int CMD_WINS = 84;

	public const int ASK_SEND = 1;

	public const int ASK_TELL = 2;

	public const int WND_SND = 32;

	public const int WND_RCV = 128;

	public const int MTU_DEF = 1200;

	public const int ACK_FAST = 3;

	public const int INTERVAL = 100;

	public const int OVERHEAD = 24;

	public const int DEADLINK = 20;

	public const int THRESH_INIT = 2;

	public const int THRESH_MIN = 2;

	public const int PROBE_INIT = 7000;

	public const int PROBE_LIMIT = 120000;

	public const int FASTACK_LIMIT = 5;

	internal int state;

	private readonly uint conv;

	internal uint mtu;

	internal uint mss;

	internal uint snd_una;

	internal uint snd_nxt;

	internal uint rcv_nxt;

	internal uint ssthresh;

	internal int rx_rttval;

	internal int rx_srtt;

	internal int rx_rto;

	internal int rx_minrto;

	internal uint snd_wnd;

	internal uint rcv_wnd;

	internal uint rmt_wnd;

	internal uint cwnd;

	internal uint probe;

	internal uint interval;

	internal uint ts_flush;

	internal uint xmit;

	internal uint nodelay;

	internal bool updated;

	internal uint ts_probe;

	internal uint probe_wait;

	internal uint dead_link;

	internal uint incr;

	internal uint current;

	internal int fastresend;

	internal int fastlimit;

	internal bool nocwnd;

	internal readonly Queue<Segment> snd_queue = new Queue<Segment>(16);

	internal readonly Queue<Segment> rcv_queue = new Queue<Segment>(16);

	internal readonly List<Segment> snd_buf = new List<Segment>(16);

	internal readonly List<Segment> rcv_buf = new List<Segment>(16);

	internal readonly List<AckItem> acklist = new List<AckItem>(16);

	internal byte[] buffer;

	private readonly Action<byte[], int> output;

	private readonly Pool<Segment> SegmentPool = new Pool<Segment>(() => new Segment(), delegate(Segment segment)
	{
		segment.Reset();
	}, 32);

	public int WaitSnd => snd_buf.Count + snd_queue.Count;

	public Kcp(uint conv, Action<byte[], int> output)
	{
		this.conv = conv;
		this.output = output;
		snd_wnd = 32u;
		rcv_wnd = 128u;
		rmt_wnd = 128u;
		mtu = 1200u;
		mss = mtu - 24;
		rx_rto = 200;
		rx_minrto = 100;
		interval = 100u;
		ts_flush = 100u;
		ssthresh = 2u;
		fastlimit = 5;
		dead_link = 20u;
		buffer = new byte[(mtu + 24) * 3];
	}

	private Segment SegmentNew()
	{
		return SegmentPool.Take();
	}

	private void SegmentDelete(Segment seg)
	{
		SegmentPool.Return(seg);
	}

	public int Receive(byte[] buffer, int len)
	{
		if (len < 0)
		{
			throw new NotSupportedException("Receive ispeek for negative len is not supported!");
		}
		if (rcv_queue.Count == 0)
		{
			return -1;
		}
		if (len < 0)
		{
			len = -len;
		}
		int num = PeekSize();
		if (num < 0)
		{
			return -2;
		}
		if (num > len)
		{
			return -3;
		}
		bool flag = rcv_queue.Count >= rcv_wnd;
		int num2 = 0;
		len = 0;
		while (rcv_queue.Count > 0)
		{
			Segment segment = rcv_queue.Dequeue();
			Buffer.BlockCopy(segment.data.GetBuffer(), 0, buffer, num2, (int)segment.data.Position);
			num2 += (int)segment.data.Position;
			len += (int)segment.data.Position;
			uint frg = segment.frg;
			SegmentDelete(segment);
			if (frg == 0)
			{
				break;
			}
		}
		int num3 = 0;
		foreach (Segment item in rcv_buf)
		{
			if (item.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
			{
				num3++;
				rcv_queue.Enqueue(item);
				rcv_nxt++;
				continue;
			}
			break;
		}
		rcv_buf.RemoveRange(0, num3);
		if (rcv_queue.Count < rcv_wnd && flag)
		{
			probe |= 2u;
		}
		return len;
	}

	public int PeekSize()
	{
		int num = 0;
		if (rcv_queue.Count == 0)
		{
			return -1;
		}
		Segment segment = rcv_queue.Peek();
		if (segment.frg == 0)
		{
			return (int)segment.data.Position;
		}
		if (rcv_queue.Count < segment.frg + 1)
		{
			return -1;
		}
		foreach (Segment item in rcv_queue)
		{
			num += (int)item.data.Position;
			if (item.frg == 0)
			{
				break;
			}
		}
		return num;
	}

	public int Send(byte[] buffer, int offset, int len)
	{
		if (len < 0)
		{
			return -1;
		}
		int num = (int)((len <= mss) ? 1 : ((len + mss - 1) / mss));
		if (num >= 128)
		{
			return -2;
		}
		if (num == 0)
		{
			num = 1;
		}
		for (int i = 0; i < num; i++)
		{
			int num2 = ((len > (int)mss) ? ((int)mss) : len);
			Segment segment = SegmentNew();
			if (len > 0)
			{
				segment.data.Write(buffer, offset, num2);
			}
			segment.frg = (byte)(num - i - 1);
			snd_queue.Enqueue(segment);
			offset += num2;
			len -= num2;
		}
		return 0;
	}

	private void UpdateAck(int rtt)
	{
		if (rx_srtt == 0)
		{
			rx_srtt = rtt;
			rx_rttval = rtt / 2;
		}
		else
		{
			int num = rtt - rx_srtt;
			if (num < 0)
			{
				num = -num;
			}
			rx_rttval = (3 * rx_rttval + num) / 4;
			rx_srtt = (7 * rx_srtt + rtt) / 8;
			if (rx_srtt < 1)
			{
				rx_srtt = 1;
			}
		}
		int value = rx_srtt + Math.Max((int)interval, 4 * rx_rttval);
		rx_rto = Utils.Clamp(value, rx_minrto, 60000);
	}

	internal void ShrinkBuf()
	{
		if (snd_buf.Count > 0)
		{
			Segment segment = snd_buf[0];
			snd_una = segment.sn;
		}
		else
		{
			snd_una = snd_nxt;
		}
	}

	internal void ParseAck(uint sn)
	{
		if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
		{
			return;
		}
		for (int i = 0; i < snd_buf.Count; i++)
		{
			Segment segment = snd_buf[i];
			if (sn == segment.sn)
			{
				snd_buf.RemoveAt(i);
				SegmentDelete(segment);
				break;
			}
			if (Utils.TimeDiff(sn, segment.sn) < 0)
			{
				break;
			}
		}
	}

	private void ParseUna(uint una)
	{
		int num = 0;
		foreach (Segment item in snd_buf)
		{
			if (Utils.TimeDiff(una, item.sn) > 0)
			{
				num++;
				SegmentDelete(item);
				continue;
			}
			break;
		}
		snd_buf.RemoveRange(0, num);
	}

	private void ParseFastack(uint sn, uint ts)
	{
		if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
		{
			return;
		}
		foreach (Segment item in snd_buf)
		{
			if (Utils.TimeDiff(sn, item.sn) < 0)
			{
				break;
			}
			if (sn != item.sn)
			{
				item.fastack++;
			}
		}
	}

	private void AckPush(uint sn, uint ts)
	{
		acklist.Add(new AckItem
		{
			serialNumber = sn,
			timestamp = ts
		});
	}

	private void ParseData(Segment newseg)
	{
		uint sn = newseg.sn;
		if (Utils.TimeDiff(sn, rcv_nxt + rcv_wnd) >= 0 || Utils.TimeDiff(sn, rcv_nxt) < 0)
		{
			SegmentDelete(newseg);
			return;
		}
		InsertSegmentInReceiveBuffer(newseg);
		MoveReceiveBufferDataToReceiveQueue();
	}

	internal void InsertSegmentInReceiveBuffer(Segment newseg)
	{
		bool flag = false;
		int num;
		for (num = rcv_buf.Count - 1; num >= 0; num--)
		{
			Segment segment = rcv_buf[num];
			if (segment.sn == newseg.sn)
			{
				flag = true;
				break;
			}
			if (Utils.TimeDiff(newseg.sn, segment.sn) > 0)
			{
				break;
			}
		}
		if (!flag)
		{
			rcv_buf.Insert(num + 1, newseg);
		}
		else
		{
			SegmentDelete(newseg);
		}
	}

	private void MoveReceiveBufferDataToReceiveQueue()
	{
		int num = 0;
		foreach (Segment item in rcv_buf)
		{
			if (item.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
			{
				num++;
				rcv_queue.Enqueue(item);
				rcv_nxt++;
				continue;
			}
			break;
		}
		rcv_buf.RemoveRange(0, num);
	}

	public int Input(byte[] data, int offset, int size)
	{
		uint earlier = snd_una;
		uint num = 0u;
		uint ts = 0u;
		int num2 = 0;
		if (data == null || size < 24)
		{
			return -1;
		}
		while (true)
		{
			uint c = 0u;
			uint c2 = 0u;
			uint c3 = 0u;
			uint c4 = 0u;
			uint c5 = 0u;
			ushort c6 = 0;
			byte c7 = 0;
			byte c8 = 0;
			if (size < 24)
			{
				break;
			}
			offset += Utils.Decode32U(data, offset, ref c5);
			if (c5 != conv)
			{
				return -1;
			}
			offset += Utils.Decode8u(data, offset, ref c7);
			offset += Utils.Decode8u(data, offset, ref c8);
			offset += Utils.Decode16U(data, offset, ref c6);
			offset += Utils.Decode32U(data, offset, ref c);
			offset += Utils.Decode32U(data, offset, ref c2);
			offset += Utils.Decode32U(data, offset, ref c4);
			offset += Utils.Decode32U(data, offset, ref c3);
			size -= 24;
			if (size < c3 || c3 < 0)
			{
				return -2;
			}
			if (c7 != 81 && c7 != 82 && c7 != 83 && c7 != 84)
			{
				return -3;
			}
			rmt_wnd = c6;
			ParseUna(c4);
			ShrinkBuf();
			switch (c7)
			{
			case 82:
				if (Utils.TimeDiff(current, c) >= 0)
				{
					UpdateAck(Utils.TimeDiff(current, c));
				}
				ParseAck(c2);
				ShrinkBuf();
				if (num2 == 0)
				{
					num2 = 1;
					num = c2;
					ts = c;
				}
				else if (Utils.TimeDiff(c2, num) > 0)
				{
					num = c2;
					ts = c;
				}
				break;
			case 81:
				if (Utils.TimeDiff(c2, rcv_nxt + rcv_wnd) >= 0)
				{
					break;
				}
				AckPush(c2, c);
				if (Utils.TimeDiff(c2, rcv_nxt) >= 0)
				{
					Segment segment = SegmentNew();
					segment.conv = c5;
					segment.cmd = c7;
					segment.frg = c8;
					segment.wnd = c6;
					segment.ts = c;
					segment.sn = c2;
					segment.una = c4;
					if (c3 != 0)
					{
						segment.data.Write(data, offset, (int)c3);
					}
					ParseData(segment);
				}
				break;
			case 83:
				probe |= 2u;
				break;
			default:
				return -3;
			case 84:
				break;
			}
			offset += (int)c3;
			size -= (int)c3;
		}
		if (num2 != 0)
		{
			ParseFastack(num, ts);
		}
		if (Utils.TimeDiff(snd_una, earlier) > 0 && cwnd < rmt_wnd)
		{
			if (cwnd < ssthresh)
			{
				cwnd++;
				incr += mss;
			}
			else
			{
				if (incr < mss)
				{
					incr = mss;
				}
				incr += mss * mss / incr + mss / 16;
				if ((cwnd + 1) * mss <= incr)
				{
					cwnd = (incr + mss - 1) / ((mss == 0) ? 1 : mss);
				}
			}
			if (cwnd > rmt_wnd)
			{
				cwnd = rmt_wnd;
				incr = rmt_wnd * mss;
			}
		}
		return 0;
	}

	private uint WndUnused()
	{
		if (rcv_queue.Count < rcv_wnd)
		{
			return rcv_wnd - (uint)rcv_queue.Count;
		}
		return 0u;
	}

	public void Flush()
	{
		int offset = 0;
		bool flag = false;
		if (!updated)
		{
			return;
		}
		Segment segment = SegmentNew();
		segment.conv = conv;
		segment.cmd = 82u;
		segment.wnd = WndUnused();
		segment.una = rcv_nxt;
		foreach (AckItem item in acklist)
		{
			MakeSpace(24);
			segment.sn = item.serialNumber;
			segment.ts = item.timestamp;
			offset += segment.Encode(buffer, offset);
		}
		acklist.Clear();
		if (rmt_wnd == 0)
		{
			if (probe_wait == 0)
			{
				probe_wait = 7000u;
				ts_probe = current + probe_wait;
			}
			else if (Utils.TimeDiff(current, ts_probe) >= 0)
			{
				if (probe_wait < 7000)
				{
					probe_wait = 7000u;
				}
				probe_wait += probe_wait / 2;
				if (probe_wait > 120000)
				{
					probe_wait = 120000u;
				}
				ts_probe = current + probe_wait;
				probe |= 1u;
			}
		}
		else
		{
			ts_probe = 0u;
			probe_wait = 0u;
		}
		if ((probe & (true ? 1u : 0u)) != 0)
		{
			segment.cmd = 83u;
			MakeSpace(24);
			offset += segment.Encode(buffer, offset);
		}
		if ((probe & 2u) != 0)
		{
			segment.cmd = 84u;
			MakeSpace(24);
			offset += segment.Encode(buffer, offset);
		}
		probe = 0u;
		uint num = Math.Min(snd_wnd, rmt_wnd);
		if (!nocwnd)
		{
			num = Math.Min(cwnd, num);
		}
		while (Utils.TimeDiff(snd_nxt, snd_una + num) < 0 && snd_queue.Count != 0)
		{
			Segment segment2 = snd_queue.Dequeue();
			segment2.conv = conv;
			segment2.cmd = 81u;
			segment2.wnd = segment.wnd;
			segment2.ts = current;
			segment2.sn = snd_nxt++;
			segment2.una = rcv_nxt;
			segment2.resendts = current;
			segment2.rto = rx_rto;
			segment2.fastack = 0u;
			segment2.xmit = 0u;
			snd_buf.Add(segment2);
		}
		uint num2 = ((fastresend > 0) ? ((uint)fastresend) : uint.MaxValue);
		uint num3 = ((nodelay == 0) ? ((uint)rx_rto >> 3) : 0u);
		int num4 = 0;
		foreach (Segment item2 in snd_buf)
		{
			bool flag2 = false;
			if (item2.xmit == 0)
			{
				flag2 = true;
				item2.xmit++;
				item2.rto = rx_rto;
				item2.resendts = (uint)((int)current + item2.rto) + num3;
			}
			else if (Utils.TimeDiff(current, item2.resendts) >= 0)
			{
				flag2 = true;
				item2.xmit++;
				xmit++;
				if (nodelay == 0)
				{
					item2.rto += Math.Max(item2.rto, rx_rto);
				}
				else
				{
					int num5 = ((nodelay < 2) ? item2.rto : rx_rto);
					item2.rto += num5 / 2;
				}
				item2.resendts = current + (uint)item2.rto;
				flag = true;
			}
			else if (item2.fastack >= num2 && (item2.xmit <= fastlimit || fastlimit <= 0))
			{
				flag2 = true;
				item2.xmit++;
				item2.fastack = 0u;
				item2.resendts = current + (uint)item2.rto;
				num4++;
			}
			if (flag2)
			{
				item2.ts = current;
				item2.wnd = segment.wnd;
				item2.una = rcv_nxt;
				int space2 = 24 + (int)item2.data.Position;
				MakeSpace(space2);
				offset += item2.Encode(buffer, offset);
				if (item2.data.Position > 0)
				{
					Buffer.BlockCopy(item2.data.GetBuffer(), 0, buffer, offset, (int)item2.data.Position);
					offset += (int)item2.data.Position;
				}
				if (item2.xmit >= dead_link)
				{
					state = -1;
				}
			}
		}
		SegmentDelete(segment);
		FlushBuffer();
		if (num4 > 0)
		{
			uint num6 = snd_nxt - snd_una;
			ssthresh = num6 / 2;
			if (ssthresh < 2)
			{
				ssthresh = 2u;
			}
			cwnd = ssthresh + num2;
			incr = cwnd * mss;
		}
		if (flag)
		{
			ssthresh = num / 2;
			if (ssthresh < 2)
			{
				ssthresh = 2u;
			}
			cwnd = 1u;
			incr = mss;
		}
		if (cwnd < 1)
		{
			cwnd = 1u;
			incr = mss;
		}
		void FlushBuffer()
		{
			if (offset > 0)
			{
				output(buffer, offset);
			}
		}
		void MakeSpace(int space)
		{
			if (offset + space > mtu)
			{
				output(buffer, offset);
				offset = 0;
			}
		}
	}

	public void Update(uint currentTimeMilliSeconds)
	{
		current = currentTimeMilliSeconds;
		if (!updated)
		{
			updated = true;
			ts_flush = current;
		}
		int num = Utils.TimeDiff(current, ts_flush);
		if (num >= 10000 || num < -10000)
		{
			ts_flush = current;
			num = 0;
		}
		if (num >= 0)
		{
			ts_flush += interval;
			if (Utils.TimeDiff(current, ts_flush) >= 0)
			{
				ts_flush = current + interval;
			}
			Flush();
		}
	}

	public uint Check(uint current_)
	{
		uint num = ts_flush;
		int num2 = int.MaxValue;
		int num3 = int.MaxValue;
		if (!updated)
		{
			return current_;
		}
		if (Utils.TimeDiff(current_, num) >= 10000 || Utils.TimeDiff(current_, num) < -10000)
		{
			num = current_;
		}
		if (Utils.TimeDiff(current_, num) >= 0)
		{
			return current_;
		}
		num2 = Utils.TimeDiff(num, current_);
		foreach (Segment item in snd_buf)
		{
			int num4 = Utils.TimeDiff(item.resendts, current_);
			if (num4 <= 0)
			{
				return current_;
			}
			if (num4 < num3)
			{
				num3 = num4;
			}
		}
		uint num5 = (uint)((num3 < num2) ? num3 : num2);
		if (num5 >= interval)
		{
			num5 = interval;
		}
		return current_ + num5;
	}

	public void SetMtu(uint mtu)
	{
		if (mtu < 50 || mtu < 24)
		{
			throw new ArgumentException("MTU must be higher than 50 and higher than OVERHEAD");
		}
		buffer = new byte[(mtu + 24) * 3];
		this.mtu = mtu;
		mss = mtu - 24;
	}

	public void SetInterval(uint interval)
	{
		if (interval > 5000)
		{
			interval = 5000u;
		}
		else if (interval < 10)
		{
			interval = 10u;
		}
		this.interval = interval;
	}

	public void SetNoDelay(uint nodelay, uint interval = 100u, int resend = 0, bool nocwnd = false)
	{
		this.nodelay = nodelay;
		if (nodelay != 0)
		{
			rx_minrto = 30;
		}
		else
		{
			rx_minrto = 100;
		}
		if (interval >= 0)
		{
			if (interval > 5000)
			{
				interval = 5000u;
			}
			else if (interval < 10)
			{
				interval = 10u;
			}
			this.interval = interval;
		}
		if (resend >= 0)
		{
			fastresend = resend;
		}
		this.nocwnd = nocwnd;
	}

	public void SetWindowSize(uint sendWindow, uint receiveWindow)
	{
		if (sendWindow != 0)
		{
			snd_wnd = sendWindow;
		}
		if (receiveWindow != 0)
		{
			rcv_wnd = Math.Max(receiveWindow, 128u);
		}
	}
}
public class Pool<T>
{
	private readonly Stack<T> objects = new Stack<T>();

	private readonly Func<T> objectGenerator;

	private readonly Action<T> objectResetter;

	public int Count => objects.Count;

	public Pool(Func<T> objectGenerator, Action<T> objectResetter, int initialCapacity)
	{
		this.objectGenerator = objectGenerator;
		this.objectResetter = objectResetter;
		for (int i = 0; i < initialCapacity; i++)
		{
			objects.Push(objectGenerator());
		}
	}

	public T Take()
	{
		if (objects.Count <= 0)
		{
			return objectGenerator();
		}
		return objects.Pop();
	}

	public void Return(T item)
	{
		objectResetter(item);
		objects.Push(item);
	}

	public void Clear()
	{
		objects.Clear();
	}
}
internal class Segment
{
	internal uint conv;

	internal uint cmd;

	internal uint frg;

	internal uint wnd;

	internal uint ts;

	internal uint sn;

	internal uint una;

	internal uint resendts;

	internal int rto;

	internal uint fastack;

	internal uint xmit;

	internal MemoryStream data = new MemoryStream(1200);

	internal int Encode(byte[] ptr, int offset)
	{
		int num = offset;
		offset += Utils.Encode32U(ptr, offset, conv);
		offset += Utils.Encode8u(ptr, offset, (byte)cmd);
		offset += Utils.Encode8u(ptr, offset, (byte)frg);
		offset += Utils.Encode16U(ptr, offset, (ushort)wnd);
		offset += Utils.Encode32U(ptr, offset, ts);
		offset += Utils.Encode32U(ptr, offset, sn);
		offset += Utils.Encode32U(ptr, offset, una);
		offset += Utils.Encode32U(ptr, offset, (uint)data.Position);
		return offset - num;
	}

	internal void Reset()
	{
		conv = 0u;
		cmd = 0u;
		frg = 0u;
		wnd = 0u;
		ts = 0u;
		sn = 0u;
		una = 0u;
		rto = 0;
		xmit = 0u;
		resendts = 0u;
		fastack = 0u;
		data.SetLength(0L);
	}
}
public static class Utils
{
	public static int Clamp(int value, int min, int max)
	{
		if (value < min)
		{
			return min;
		}
		if (value > max)
		{
			return max;
		}
		return value;
	}

	public static int Encode8u(byte[] p, int offset, byte c)
	{
		p[offset] = c;
		return 1;
	}

	public static int Decode8u(byte[] p, int offset, ref byte c)
	{
		c = p[offset];
		return 1;
	}

	public static int Encode16U(byte[] p, int offset, ushort w)
	{
		p[offset] = (byte)w;
		p[1 + offset] = (byte)(w >> 8);
		return 2;
	}

	public static int Decode16U(byte[] p, int offset, ref ushort c)
	{
		ushort num = 0;
		num |= p[offset];
		num |= (ushort)(p[1 + offset] << 8);
		c = num;
		return 2;
	}

	public static int Encode32U(byte[] p, int offset, uint l)
	{
		p[offset] = (byte)l;
		p[1 + offset] = (byte)(l >> 8);
		p[2 + offset] = (byte)(l >> 16);
		p[3 + offset] = (byte)(l >> 24);
		return 4;
	}

	public static int Decode32U(byte[] p, int offset, ref uint c)
	{
		uint num = 0u;
		num |= p[offset];
		num |= (uint)(p[1 + offset] << 8);
		num |= (uint)(p[2 + offset] << 16);
		num |= (uint)(p[3 + offset] << 24);
		c = num;
		return 4;
	}

	[MethodImpl(MethodImplOptions.AggressiveInlining)]
	public static int TimeDiff(uint later, uint earlier)
	{
		return (int)(later - earlier);
	}
}