Decompiled source of TechtonicaDirectConnect v1.0.103

plugins/TechtonicaDirectConnect.dll

Decompiled 7 months 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.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 Mirror.FizzySteam;
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
{
	public static class InventorySyncPatches
	{
		private static bool _patchesApplied;

		private static object _lastHitDestructibleAction;

		private static object _lastMOLEAction;

		private static object _lastTakeAllAction;

		private static ManualLogSource Log => Plugin.Log;

		public static void ApplyPatches(Harmony harmony)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			//IL_0295: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Expected O, but got Unknown
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Expected O, but got Unknown
			//IL_0340: Unknown result type (might be due to invalid IL or missing references)
			//IL_0347: Expected O, but got Unknown
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Expected O, but got Unknown
			if (_patchesApplied)
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = AccessTools.Method("TakeAllInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					HarmonyMethod val = new HarmonyMethod(typeof(InventorySyncPatches), "TakeAllInfo_ProcessOnClient_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched TakeAllInfo.ProcessOnClient");
				}
				MethodInfo methodInfo2 = AccessTools.Method("HitDestructibleInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo2 != null)
				{
					HarmonyMethod val2 = new HarmonyMethod(typeof(InventorySyncPatches), "HitDestructibleInfo_ProcessOnClient_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo2, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched HitDestructibleInfo.ProcessOnClient");
				}
				MethodInfo methodInfo3 = AccessTools.Method("MOLEActionInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo3 != null)
				{
					HarmonyMethod val3 = new HarmonyMethod(typeof(InventorySyncPatches), "MOLEActionInfo_ProcessOnClient_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo3, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched MOLEActionInfo.ProcessOnClient (prefix)");
				}
				MethodInfo methodInfo4 = AccessTools.Method("CraftActionInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo4 == null)
				{
					methodInfo4 = AccessTools.Method("CraftInfo:ProcessOnClient", (Type[])null, (Type[])null);
				}
				if (methodInfo4 != null)
				{
					HarmonyMethod val4 = new HarmonyMethod(typeof(InventorySyncPatches), "CraftInfo_ProcessOnClient_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo4, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("[InventorySync] Patched " + methodInfo4.DeclaringType.Name + ".ProcessOnClient"));
				}
				else
				{
					Log.LogWarning((object)"[InventorySync] CraftActionInfo/CraftInfo.ProcessOnClient not found");
				}
				MethodInfo methodInfo5 = AccessTools.Method("NetworkBuildInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo5 != null)
				{
					HarmonyMethod val5 = new HarmonyMethod(typeof(InventorySyncPatches), "SimpleBuildInfo_ProcessOnClient_Postfix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, val5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("[InventorySync] Patched " + methodInfo5.DeclaringType.Name + ".ProcessOnClient"));
				}
				else
				{
					Log.LogWarning((object)"[InventorySync] NetworkBuildInfo.ProcessOnClient not found");
				}
				MethodInfo methodInfo6 = AccessTools.Method("ExchangeMachineActionInfo:ProcessOnClient", (Type[])null, (Type[])null);
				if (methodInfo6 == null)
				{
					methodInfo6 = AccessTools.Method("ExchangeMachineInfo:ProcessOnClient", (Type[])null, (Type[])null);
				}
				if (methodInfo6 != null)
				{
					HarmonyMethod val6 = new HarmonyMethod(typeof(InventorySyncPatches), "ExchangeMachineInfo_ProcessOnClient_Postfix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, val6, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("[InventorySync] Patched " + methodInfo6.DeclaringType.Name + ".ProcessOnClient"));
				}
				else
				{
					Log.LogWarning((object)"[InventorySync] ExchangeMachineActionInfo/ExchangeMachineInfo.ProcessOnClient not found");
				}
				MethodInfo methodInfo7 = AccessTools.Method("NetworkMessageRelay:UserCode_HitDestructableResponse", (Type[])null, (Type[])null);
				if (methodInfo7 != null)
				{
					HarmonyMethod val7 = new HarmonyMethod(typeof(InventorySyncPatches), "HitDestructableResponse_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo7, val7, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_HitDestructableResponse");
				}
				else
				{
					Log.LogWarning((object)"[InventorySync] UserCode_HitDestructableResponse not found");
				}
				MethodInfo methodInfo8 = AccessTools.Method("NetworkMessageRelay:UserCode_HitVoxelResponse", (Type[])null, (Type[])null);
				if (methodInfo8 != null)
				{
					HarmonyMethod val8 = new HarmonyMethod(typeof(InventorySyncPatches), "HitVoxelResponse_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo8, val8, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_HitVoxelResponse");
				}
				MethodInfo methodInfo9 = AccessTools.Method("NetworkMessageRelay:UserCode_MOLEResponse", (Type[])null, (Type[])null);
				if (methodInfo9 != null)
				{
					HarmonyMethod val9 = new HarmonyMethod(typeof(InventorySyncPatches), "MOLEResponse_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo9, val9, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_MOLEResponse");
				}
				MethodInfo methodInfo10 = AccessTools.Method("NetworkMessageRelay:UserCode_TakeAllResponse", (Type[])null, (Type[])null);
				if (methodInfo10 != null)
				{
					HarmonyMethod val10 = new HarmonyMethod(typeof(InventorySyncPatches), "TakeAllResponse_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo10, val10, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"[InventorySync] Patched NetworkMessageRelay.UserCode_TakeAllResponse");
				}
				PatchIncomingActionRPCs(harmony);
				_patchesApplied = true;
				Log.LogInfo((object)"[InventorySync] All inventory sync patches applied successfully");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[InventorySync] Error applying patches: " + ex.Message));
			}
		}

		private static void PatchIncomingActionRPCs(Harmony harmony)
		{
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("NetworkMessageRelay");
			if (type == null)
			{
				Log.LogWarning((object)"[InventorySync] NetworkMessageRelay type not found, cannot patch incoming RPCs");
				return;
			}
			string[] obj = new string[20]
			{
				"HitDestructable", "ActivateMachine", "MOLEAction", "TakeAllFromMachine", "Craft", "RpcBuildSimpleMachine", "RpcBuildConveyor", "EraseMachine", "RotateMachine", "SetFilter",
				"HarvestPlant", "ExchangeMachineInventory", "SwapVariant", "Replace", "Shoot", "Ping", "ActivatePower", "DeactivateTech", "TempConveyor", "HitVoxel"
			};
			int num = 0;
			string[] array = obj;
			foreach (string text in array)
			{
				MethodInfo method = type.GetMethod(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method == null)
				{
					method = type.GetMethod("UserCode_" + text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if (method != null)
				{
					try
					{
						HarmonyMethod val = new HarmonyMethod(typeof(InventorySyncPatches), "FixIncomingActionTick_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						num++;
						Log.LogInfo((object)("[InventorySync] Patched incoming RPC: " + method.Name));
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("[InventorySync] Failed to patch " + text + ": " + ex.Message));
					}
				}
			}
			Log.LogInfo((object)$"[InventorySync] Patched {num} incoming action RPCs for tick fix");
		}

		public static void FixIncomingActionTick_Prefix(object __0, MethodBase __originalMethod)
		{
			try
			{
				Log.LogInfo((object)("[InventorySync] RPC PREFIX CALLED: " + (__originalMethod?.Name ?? "null") + ", arg0=" + (__0?.GetType()?.Name ?? "null")));
				if (!NetworkClient.active || NetworkServer.active)
				{
					Log.LogInfo((object)"[InventorySync] Skipping - not a client");
					return;
				}
				if (__0 == null)
				{
					Log.LogInfo((object)"[InventorySync] No info object in first parameter");
					return;
				}
				string name = __0.GetType().Name;
				Log.LogInfo((object)("[InventorySync] Processing info object of type: " + name));
				Type type = AccessTools.TypeByName("MachineManager");
				if (type == null)
				{
					Log.LogWarning((object)"[InventorySync] MachineManager type not found");
					return;
				}
				object obj = type.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					Log.LogWarning((object)"[InventorySync] MachineManager.instance is null");
					return;
				}
				FieldInfo field = type.GetField("curTick", BindingFlags.Instance | BindingFlags.Public);
				if (field == null)
				{
					Log.LogWarning((object)"[InventorySync] curTick field not found");
					return;
				}
				int num = (int)field.GetValue(obj);
				FieldInfo field2 = __0.GetType().GetField("tick", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field2 != null)
				{
					int num2 = (int)field2.GetValue(__0);
					Log.LogInfo((object)$"[InventorySync] {__originalMethod?.Name}: oldTick={num2}, currentTick={num}");
					if (num2 != num)
					{
						field2.SetValue(__0, num);
						Log.LogInfo((object)$"[InventorySync] FIXED incoming {__originalMethod?.Name} tick: {num2} -> {num}");
					}
				}
				else
				{
					Log.LogWarning((object)("[InventorySync] tick field not found on " + name));
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] FixIncomingActionTick error: " + ex.Message));
			}
		}

		public static void StorePendingAction(object action)
		{
			if (action != null)
			{
				switch (action.GetType().Name)
				{
				case "HitDestructibleAction":
					_lastHitDestructibleAction = action;
					break;
				case "MOLEAction":
					_lastMOLEAction = action;
					break;
				case "TakeAllAction":
					_lastTakeAllAction = action;
					break;
				}
			}
		}

		public static bool HitDestructableResponse_Prefix(object[] __args)
		{
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					return true;
				}
				Array array = ((__args.Length > 1) ? (__args[1] as Array) : null);
				if (array != null && array.Length > 0)
				{
					Log.LogInfo((object)$"[InventorySync] HitDestructableResponse: Got {array.Length} items from server, using original handler");
					return true;
				}
				Log.LogInfo((object)"[InventorySync] HitDestructableResponse: Empty response from headless server, calculating items locally");
				Type type = AccessTools.TypeByName("Player");
				object obj = null;
				obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null && (Object)(object)NetworkClient.localPlayer != (Object)null)
				{
					NetworkIdentity localPlayer = NetworkClient.localPlayer;
					if ((Object)(object)localPlayer != (Object)null && (Object)(object)((Component)localPlayer).gameObject != (Object)null)
					{
						MethodInfo method = typeof(GameObject).GetMethod("GetComponent", new Type[0]);
						if (method != null && type != null)
						{
							obj = method.MakeGenericMethod(type).Invoke(((Component)localPlayer).gameObject, null);
							if (obj != null)
							{
								Log.LogInfo((object)"[InventorySync] Found Player via NetworkClient.localPlayer");
							}
						}
					}
				}
				if (obj == null && type != null)
				{
					MethodInfo method2 = typeof(Object).GetMethod("FindObjectOfType", new Type[0]);
					if (method2 != null)
					{
						obj = method2.MakeGenericMethod(type).Invoke(null, null);
						if (obj != null)
						{
							Log.LogInfo((object)"[InventorySync] Found Player via FindObjectOfType");
						}
					}
				}
				if (obj == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructableResponse: Could not find Player through any method");
					return false;
				}
				object obj2 = type.GetProperty("networkedPlayer")?.GetValue(obj);
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructableResponse: networkedPlayer is null");
					return false;
				}
				object obj3 = obj2.GetType().GetField("pendingActions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2);
				object obj4 = null;
				if (obj3 != null && obj3 is IEnumerable enumerable)
				{
					foreach (object item in enumerable)
					{
						if (item?.GetType()?.Name == "HitDestructibleAction")
						{
							obj4 = item.GetType().GetMethod("GetInfo")?.Invoke(item, null);
							break;
						}
					}
				}
				if (obj4 == null && _lastHitDestructibleAction != null)
				{
					obj4 = _lastHitDestructibleAction.GetType().GetMethod("GetInfo")?.Invoke(_lastHitDestructibleAction, null);
				}
				if (obj4 == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructableResponse: Could not find pending HitDestructible action");
					ClearPendingHitDestructible(obj2);
					return false;
				}
				CalculateAndAddHitDestructibleRewards(obj4, obj);
				ClearPendingHitDestructible(obj2);
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] HitDestructableResponse_Prefix error: " + ex.Message));
				return true;
			}
		}

		private static void ClearPendingHitDestructible(object networkedPlayer)
		{
			try
			{
				networkedPlayer.GetType().GetMethod("ClearPendingAction", BindingFlags.Instance | BindingFlags.Public);
				_lastHitDestructibleAction = null;
			}
			catch
			{
			}
		}

		private static void CalculateAndAddHitDestructibleRewards(object hitDestructibleInfo, object playerInstance)
		{
			try
			{
				if (!(hitDestructibleInfo.GetType().GetField("propLookups")?.GetValue(hitDestructibleInfo) is Array array) || array.Length == 0)
				{
					Log.LogInfo((object)"[InventorySync] CalculateRewards: No propLookups");
					return;
				}
				FieldInfo field = hitDestructibleInfo.GetType().GetField("hitStrength");
				float num = ((field != null) ? ((float)field.GetValue(hitDestructibleInfo)) : 100f);
				Type type = AccessTools.TypeByName("PropManager");
				object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					Log.LogWarning((object)"[InventorySync] CalculateRewards: PropManager.instance is null");
					return;
				}
				object obj2 = AccessTools.TypeByName("Player").GetProperty("inventory")?.GetValue(playerInstance);
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] CalculateRewards: Player.inventory is null");
					return;
				}
				Type type2 = AccessTools.TypeByName("DestructibleData");
				MethodInfo method = type.GetMethod("GetDestructibleData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					typeof(int),
					type2.MakeByRefType()
				}, null);
				Type type3 = AccessTools.TypeByName("UIManager");
				object obj3 = type3?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				object obj4 = (type3?.GetField("hud", BindingFlags.Instance | BindingFlags.Public))?.GetValue(obj3);
				int num2 = 0;
				bool flag = default(bool);
				for (int i = 0; i < array.Length; i++)
				{
					object value = array.GetValue(i);
					FieldInfo field2 = value.GetType().GetField("typeID");
					if (field2 == null)
					{
						continue;
					}
					int num3 = (int)field2.GetValue(value);
					object[] array2 = new object[2] { num3, null };
					object obj5 = method?.Invoke(obj, array2);
					int num4;
					if (obj5 is bool)
					{
						flag = (bool)obj5;
						num4 = 1;
					}
					else
					{
						num4 = 0;
					}
					if (((uint)num4 & (flag ? 1u : 0u)) == 0)
					{
						continue;
					}
					object obj6 = array2[1];
					if (obj6 == null)
					{
						continue;
					}
					MethodInfo method2 = obj6.GetType().GetMethod("WouldBeDestroyed");
					bool flag2 = true;
					if (method2 != null)
					{
						flag2 = (bool)method2.Invoke(obj6, new object[2] { value, num });
					}
					if (!flag2 || !(obj6.GetType().GetField("rewards")?.GetValue(obj6) is IList list) || list.Count <= 0)
					{
						continue;
					}
					foreach (object item in list)
					{
						FieldInfo? field3 = item.GetType().GetField("resource");
						FieldInfo field4 = item.GetType().GetField("quantity");
						object obj7 = field3?.GetValue(item);
						object obj8 = field4?.GetValue(item);
						if (obj7 != null && obj8 != null)
						{
							MethodInfo method3 = obj2.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
							{
								obj7.GetType(),
								typeof(int)
							}, null);
							if (method3 != null)
							{
								method3.Invoke(obj2, new object[2] { obj7, obj8 });
								num2++;
								obj4?.GetType().GetMethod("OnCollectResources")?.Invoke(obj4, new object[2] { obj7, obj8 });
								Log.LogInfo((object)$"[InventorySync] Added reward: {obj7} x{obj8}");
							}
						}
					}
				}
				Log.LogInfo((object)$"[InventorySync] CalculateRewards: Added {num2} reward items");
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] CalculateRewards error: " + ex.Message));
			}
		}

		public static bool HitVoxelResponse_Prefix(object[] __args)
		{
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					return true;
				}
				Array array = ((__args.Length > 3) ? (__args[3] as Array) : null);
				if (array != null && array.Length > 0)
				{
					return true;
				}
				Log.LogInfo((object)"[InventorySync] HitVoxelResponse: Empty response, client will calculate ore locally");
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] HitVoxelResponse_Prefix error: " + ex.Message));
				return true;
			}
		}

		public static bool MOLEResponse_Prefix(object[] __args)
		{
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					return true;
				}
				Array array = ((__args.Length > 1) ? (__args[1] as Array) : null);
				if (array != null && array.Length > 0)
				{
					return true;
				}
				Log.LogInfo((object)"[InventorySync] MOLEResponse: Empty response, client handles ore locally");
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] MOLEResponse_Prefix error: " + ex.Message));
				return true;
			}
		}

		public static bool TakeAllResponse_Prefix(object[] __args)
		{
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					return true;
				}
				Array array = ((__args.Length > 1) ? (__args[1] as Array) : null);
				if (array != null && array.Length > 0)
				{
					return true;
				}
				Log.LogInfo((object)"[InventorySync] TakeAllResponse: Empty response from headless server");
				return false;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] TakeAllResponse_Prefix error: " + ex.Message));
				return true;
			}
		}

		public static bool TakeAllInfo_ProcessOnClient_Prefix(object __instance)
		{
			Log.LogInfo((object)("[InventorySync] TakeAllInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name));
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					Log.LogInfo((object)$"[InventorySync] TakeAll: Skipping - NetworkClient.active={NetworkClient.active}, NetworkServer.active={NetworkServer.active}");
					return true;
				}
				MethodInfo method = __instance.GetType().GetMethod("TakeAll");
				if (method == null)
				{
					Log.LogWarning((object)"[InventorySync] TakeAll: TakeAll method not found!");
					return true;
				}
				Log.LogInfo((object)"[InventorySync] TakeAll: Calling TakeAll() method...");
				object obj = method.Invoke(__instance, null);
				Log.LogInfo((object)("[InventorySync] TakeAll: TakeAll() returned: " + (obj?.GetType()?.Name ?? "null")));
				IList list = obj as IList;
				if (list == null || list.Count == 0)
				{
					Log.LogInfo((object)$"[InventorySync] TakeAll: No items returned (items={list != null}, count={list?.Count ?? 0})");
					return false;
				}
				Log.LogInfo((object)$"[InventorySync] TakeAll: Got {list.Count} item stacks to add");
				Type type = AccessTools.TypeByName("Player");
				Log.LogInfo((object)("[InventorySync] TakeAll: playerType=" + (type?.Name ?? "null")));
				object obj2 = null;
				obj2 = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj2 != null)
				{
					Log.LogInfo((object)"[InventorySync] TakeAll: Got player via Player.instance");
				}
				if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null)
				{
					obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type);
					if (obj2 != null)
					{
						Log.LogInfo((object)"[InventorySync] TakeAll: Got player via NetworkClient.localPlayer");
					}
				}
				if (obj2 == null)
				{
					obj2 = Object.FindObjectOfType(type);
					if (obj2 != null)
					{
						Log.LogInfo((object)"[InventorySync] TakeAll: Got player via FindObjectOfType");
					}
				}
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] TakeAll: Could not find Player instance via any method!");
					return false;
				}
				object obj3 = type.GetProperty("inventory")?.GetValue(obj2);
				if (obj3 == null)
				{
					Log.LogWarning((object)"[InventorySync] TakeAll: Player.inventory is NULL!");
					return false;
				}
				Log.LogInfo((object)("[InventorySync] TakeAll: Got inventory of type " + obj3.GetType().Name));
				MethodInfo method2 = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					AccessTools.TypeByName("ResourceInfo"),
					typeof(int)
				}, null);
				if (method2 == null)
				{
					method2 = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public);
					Log.LogInfo((object)$"[InventorySync] TakeAll: AddResources fallback lookup = {method2 != null}");
				}
				Log.LogInfo((object)("[InventorySync] TakeAll: AddResources method = " + (method2?.Name ?? "null")));
				int num = 0;
				foreach (object item in list)
				{
					PropertyInfo? property = item.GetType().GetProperty("info");
					FieldInfo field = item.GetType().GetField("count");
					object obj4 = property?.GetValue(item);
					object obj5 = field?.GetValue(item);
					Log.LogInfo((object)string.Format("[InventorySync] TakeAll: Item - info={0}, count={1}, infoType={2}", obj4 != null, obj5, obj4?.GetType()?.Name ?? "null"));
					if (obj4 != null && obj5 != null)
					{
						try
						{
							method2?.Invoke(obj3, new object[2] { obj4, obj5 });
							num++;
							Log.LogInfo((object)$"[InventorySync] TakeAll: Added item #{num}");
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("[InventorySync] Failed to add resource: " + ex.Message));
						}
					}
				}
				Log.LogInfo((object)$"[InventorySync] TakeAll: Added {num} item stacks to inventory");
				return false;
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)("[InventorySync] TakeAllInfo_Prefix error: " + ex2.Message));
				return true;
			}
		}

		public static void HitDestructibleInfo_ProcessOnClient_Prefix(object __instance)
		{
			Log.LogInfo((object)("[InventorySync] HitDestructibleInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name));
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					Log.LogInfo((object)$"[InventorySync] HitDestructible: Skipping - NetworkClient.active={NetworkClient.active}, NetworkServer.active={NetworkServer.active}");
					return;
				}
				FieldInfo field = __instance.GetType().GetField("propLookups");
				if (field == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructible: propLookups field not found!");
					return;
				}
				Array array = field.GetValue(__instance) as Array;
				if (array == null || array.Length == 0)
				{
					Log.LogInfo((object)$"[InventorySync] HitDestructible: No propLookups (null={array == null})");
					return;
				}
				Log.LogInfo((object)$"[InventorySync] HitDestructible: Processing {array.Length} prop lookups");
				Type type = AccessTools.TypeByName("PropManager");
				object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructible: PropManager.instance is NULL!");
					return;
				}
				Type type2 = AccessTools.TypeByName("Player");
				object obj2 = null;
				obj2 = type2?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null)
				{
					obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type2);
				}
				if (obj2 == null)
				{
					obj2 = Object.FindObjectOfType(type2);
				}
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructible: Could not find Player!");
					return;
				}
				Log.LogInfo((object)"[InventorySync] HitDestructible: Got player instance");
				object obj3 = type2.GetProperty("inventory")?.GetValue(obj2);
				if (obj3 == null)
				{
					Log.LogWarning((object)"[InventorySync] HitDestructible: Player.inventory is NULL!");
					return;
				}
				Type type3 = AccessTools.TypeByName("DestructibleData");
				MethodInfo methodInfo = null;
				if (type3 != null)
				{
					methodInfo = type.GetMethod("GetDestructibleData", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
					{
						typeof(int),
						type3.MakeByRefType()
					}, null);
					Log.LogInfo((object)$"[InventorySync] HitDestructible: GetDestructibleData(int, out) method found: {methodInfo != null}");
				}
				else
				{
					Log.LogWarning((object)"[InventorySync] HitDestructible: DestructibleData type not found");
				}
				int num = 0;
				bool flag = default(bool);
				for (int i = 0; i < array.Length; i++)
				{
					object value = array.GetValue(i);
					try
					{
						FieldInfo field2 = value.GetType().GetField("typeID");
						if (field2 == null)
						{
							Log.LogWarning((object)"[InventorySync] HitDestructible: typeID field not found in InstanceLookup");
							continue;
						}
						int num2 = (int)field2.GetValue(value);
						object[] array2 = new object[2] { num2, null };
						object obj4 = methodInfo?.Invoke(obj, array2);
						int num3;
						if (obj4 is bool)
						{
							flag = (bool)obj4;
							num3 = 1;
						}
						else
						{
							num3 = 0;
						}
						if (((uint)num3 & (flag ? 1u : 0u)) == 0)
						{
							continue;
						}
						object obj5 = array2[1];
						if (obj5 == null || !(obj5.GetType().GetField("rewards")?.GetValue(obj5) is IList list) || list.Count <= 0)
						{
							continue;
						}
						foreach (object item in list)
						{
							FieldInfo? field3 = item.GetType().GetField("resource");
							FieldInfo field4 = item.GetType().GetField("quantity");
							object obj6 = field3?.GetValue(item);
							object obj7 = field4?.GetValue(item);
							if (obj6 == null || obj7 == null)
							{
								continue;
							}
							try
							{
								MethodInfo method = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
								{
									obj6.GetType(),
									typeof(int)
								}, null);
								if (method != null)
								{
									method.Invoke(obj3, new object[2] { obj6, obj7 });
									num++;
								}
								else
								{
									Log.LogWarning((object)("[InventorySync] HitDestructible: AddResources method not found for type " + obj6.GetType().Name));
								}
							}
							catch (Exception ex)
							{
								Log.LogWarning((object)("[InventorySync] HitDestructible: AddResources failed: " + ex.Message));
							}
						}
					}
					catch
					{
					}
				}
				if (num > 0)
				{
					Log.LogInfo((object)$"[InventorySync] HitDestructible: Added {num} item stacks to inventory");
				}
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)("[InventorySync] HitDestructibleInfo_Prefix error: " + ex2.Message));
			}
		}

		public static void MOLEActionInfo_ProcessOnClient_Prefix(object __instance)
		{
			try
			{
				if (NetworkClient.active && !NetworkServer.active)
				{
					FieldInfo field = __instance.GetType().GetField("canMineOre");
					if (field != null)
					{
						bool flag = (bool)field.GetValue(__instance);
						field.SetValue(__instance, true);
						Log.LogInfo((object)$"[InventorySync] MOLE: Set canMineOre={flag}->true (client will check locally)");
					}
					else
					{
						Log.LogWarning((object)"[InventorySync] MOLE: canMineOre field not found!");
					}
					FieldInfo field2 = __instance.GetType().GetField("digPos");
					if (field2 != null)
					{
						object value = field2.GetValue(__instance);
						Log.LogInfo((object)$"[InventorySync] MOLE: digPos={value}");
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] MOLEActionInfo_Prefix error: " + ex.Message));
			}
		}

		public static bool CraftInfo_ProcessOnClient_Prefix(object __instance)
		{
			Log.LogInfo((object)("[InventorySync] CraftInfo_ProcessOnClient_Prefix CALLED! instance=" + __instance?.GetType()?.Name));
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					Log.LogInfo((object)"[InventorySync] Craft: Skipping - not remote client");
					return true;
				}
				FieldInfo[] fields = __instance.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public);
				string text = string.Join(", ", Array.ConvertAll(fields, (FieldInfo f) => $"{f.Name}={f.GetValue(__instance)}"));
				Log.LogInfo((object)("[InventorySync] CraftInfo fields: " + text));
				Type type = AccessTools.TypeByName("Player");
				object obj = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null && (Object)(object)NetworkClient.localPlayer != (Object)null)
				{
					obj = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type);
				}
				if (obj == null)
				{
					obj = Object.FindObjectOfType(type);
				}
				if (obj == null)
				{
					Log.LogWarning((object)"[InventorySync] Craft: Could not find Player!");
					return true;
				}
				object obj2 = type.GetProperty("crafting")?.GetValue(obj);
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] Craft: Player.crafting is null!");
					return true;
				}
				object obj3 = __instance.GetType().GetField("recipe")?.GetValue(__instance);
				if (obj3 != null)
				{
					Log.LogInfo((object)("[InventorySync] Craft: Found recipe of type " + obj3.GetType().Name));
					MethodInfo method = obj2.GetType().GetMethod("Craft", BindingFlags.Instance | BindingFlags.Public);
					if (method != null)
					{
						Log.LogInfo((object)"[InventorySync] Craft: Calling Craft method...");
						try
						{
							method.Invoke(obj2, new object[1] { obj3 });
							Log.LogInfo((object)"[InventorySync] Craft: Craft method executed!");
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("[InventorySync] Craft method failed: " + (ex.InnerException?.Message ?? ex.Message)));
						}
					}
					else
					{
						MethodInfo[] methods = obj2.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
						string text2 = string.Join(", ", Array.ConvertAll(methods, (MethodInfo m) => m.Name));
						Log.LogInfo((object)("[InventorySync] Craft: Available methods: " + text2));
					}
				}
				return true;
			}
			catch (Exception ex2)
			{
				Log.LogWarning((object)("[InventorySync] CraftInfo_Prefix error: " + ex2.Message));
				return true;
			}
		}

		public static void SimpleBuildInfo_ProcessOnClient_Postfix(object __instance)
		{
			try
			{
				if (NetworkClient.active && !NetworkServer.active)
				{
					Log.LogInfo((object)"[InventorySync] SimpleBuildInfo postfix - build action processed");
					FieldInfo field = __instance.GetType().GetField("machineType");
					if (!(field == null))
					{
						field.GetValue(__instance);
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] SimpleBuildInfo_Postfix error: " + ex.Message));
			}
		}

		public static void ExchangeMachineInfo_ProcessOnClient_Postfix(object __instance)
		{
			try
			{
				if (!NetworkClient.active || NetworkServer.active)
				{
					return;
				}
				FieldInfo field = __instance.GetType().GetField("addingToMachine");
				FieldInfo field2 = __instance.GetType().GetField("quantity");
				FieldInfo field3 = __instance.GetType().GetField("resID");
				if (field == null || field2 == null || field3 == null)
				{
					Log.LogWarning((object)"[InventorySync] ExchangeMachine: Required fields not found");
					return;
				}
				bool num = (bool)field.GetValue(__instance);
				int num2 = (int)field2.GetValue(__instance);
				int num3 = (int)field3.GetValue(__instance);
				if (num)
				{
					return;
				}
				Log.LogInfo((object)$"[InventorySync] ExchangeMachine: Taking {num2} of resID {num3} from machine");
				MethodInfo methodInfo = AccessTools.TypeByName("SaveState")?.GetMethod("GetResInfoFromId", BindingFlags.Static | BindingFlags.Public);
				if (methodInfo == null)
				{
					Log.LogWarning((object)"[InventorySync] ExchangeMachine: GetResInfoFromId not found");
					return;
				}
				object obj = methodInfo.Invoke(null, new object[1] { num3 });
				if (obj == null)
				{
					Log.LogWarning((object)$"[InventorySync] ExchangeMachine: No ResourceInfo for resID {num3}");
					return;
				}
				Type type = AccessTools.TypeByName("Player");
				object obj2 = null;
				obj2 = type?.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj2 == null && (Object)(object)NetworkClient.localPlayer != (Object)null)
				{
					obj2 = ((Component)NetworkClient.localPlayer).gameObject.GetComponent(type);
				}
				if (obj2 == null)
				{
					obj2 = Object.FindObjectOfType(type);
				}
				if (obj2 == null)
				{
					Log.LogWarning((object)"[InventorySync] ExchangeMachine: Could not find Player!");
					return;
				}
				object obj3 = type.GetProperty("inventory")?.GetValue(obj2);
				if (obj3 == null)
				{
					Log.LogWarning((object)"[InventorySync] ExchangeMachine: Player.inventory is null!");
					return;
				}
				MethodInfo method = obj3.GetType().GetMethod("AddResources", BindingFlags.Instance | BindingFlags.Public, null, new Type[2]
				{
					obj.GetType(),
					typeof(int)
				}, null);
				if (method != null)
				{
					method.Invoke(obj3, new object[2] { obj, num2 });
					Log.LogInfo((object)$"[InventorySync] ExchangeMachine: Added {num2} items to inventory");
				}
				else
				{
					Log.LogWarning((object)("[InventorySync] ExchangeMachine: AddResources method not found for type " + obj.GetType().Name));
				}
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[InventorySync] ExchangeMachineInfo_Postfix error: " + ex.Message));
			}
		}
	}
	[BepInPlugin("com.certifried.techtonicadirectconnect", "Techtonica Direct Connect", "1.0.103")]
	[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;

		private string _pendingServerAddress = "";

		private int _pendingServerPort = 6968;

		private static float _loadingStuckTimer = 0f;

		private static bool _loadingMonitorActive = false;

		private static string _lastLoadingState = "";

		private static bool _finishLoadingCalled = false;

		private static int _lastLoggedSecond = -1;

		private static int _updateHeartbeat = 0;

		private static bool _loadingMonitorDebugLogged = false;

		private static int _monitorCheckCount = 0;

		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);
			InventorySyncPatches.ApplyPatches(_harmony);
			TickFixPatches.ApplyPatches(_harmony);
			Log.LogInfo((object)"[Techtonica Direct Connect] v1.0.103 loaded!");
			Log.LogInfo((object)"[Techtonica Direct Connect] Press F11 to open connect dialog");
		}

		private void Update()
		{
			_updateHeartbeat++;
			if (!_updateRunning)
			{
				_updateRunning = true;
				Log.LogInfo((object)"[DirectConnect] Update() is running!");
			}
			if (_updateHeartbeat % 300 == 0 && NetworkClient.active)
			{
				Log.LogInfo((object)$"[DirectConnect] HEARTBEAT: frame={_updateHeartbeat}, monitorActive={_loadingMonitorActive}, timer={_loadingStuckTimer:F1}s, finishCalled={_finishLoadingCalled}");
			}
			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;
			CheckLoadingMonitor();
		}

		private void CheckLoadingMonitor()
		{
			_monitorCheckCount++;
			if (!NetworkClient.active)
			{
				if (_monitorCheckCount % 300 == 0 && _loadingMonitorActive)
				{
					Log.LogWarning((object)"[DirectConnect] Monitor: NetworkClient not active!");
				}
				return;
			}
			if (_finishLoadingCalled)
			{
				if (_monitorCheckCount % 300 == 0)
				{
					Log.LogInfo((object)"[DirectConnect] Monitor: finish already called, skipping");
				}
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("LoadingUI");
				if (type == null)
				{
					if (!_loadingMonitorDebugLogged)
					{
						Log.LogWarning((object)"[DirectConnect] LoadingUI type not found!");
						_loadingMonitorDebugLogged = true;
					}
					return;
				}
				object obj = null;
				FieldInfo fieldInfo = AccessTools.Field(type, "instance");
				if (fieldInfo != null)
				{
					obj = fieldInfo.GetValue(null);
				}
				if (obj == null)
				{
					PropertyInfo propertyInfo = AccessTools.Property(type, "Instance");
					if (propertyInfo != null)
					{
						obj = propertyInfo.GetValue(null);
					}
				}
				if (obj == null)
				{
					obj = Object.FindObjectOfType(type);
				}
				if (obj == null)
				{
					if (_loadingMonitorActive && _monitorCheckCount % 60 == 0)
					{
						Log.LogWarning((object)"[DirectConnect] Monitor: LoadingUI instance became null!");
					}
					else if (!_loadingMonitorDebugLogged)
					{
						FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						Log.LogWarning((object)("[DirectConnect] LoadingUI.instance is null. Available fields: " + string.Join(", ", fields.Select((FieldInfo f) => f.Name))));
						_loadingMonitorDebugLogged = true;
					}
					return;
				}
				Component val = (Component)((obj is Component) ? obj : null);
				bool flag = (Object)(object)val != (Object)null && val.gameObject.activeInHierarchy;
				bool flag2 = false;
				FieldInfo field = type.GetField("_loaded", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					try
					{
						flag2 = (bool)field.GetValue(obj);
					}
					catch
					{
					}
				}
				bool flag3 = flag && !flag2;
				if (_monitorCheckCount % 60 == 0 && _loadingMonitorActive)
				{
					Log.LogInfo((object)$"[DirectConnect] Monitor check: goActive={flag}, _loaded={flag2}, isActive={flag3}, timer={_loadingStuckTimer:F1}s");
				}
				if (!flag3)
				{
					if (_loadingMonitorActive)
					{
						Log.LogWarning((object)$"[DirectConnect] Monitor: Loading done or inactive! goActive={flag}, _loaded={flag2}");
					}
					_loadingMonitorActive = false;
					_loadingStuckTimer = 0f;
					return;
				}
				string text = "";
				string[] array = new string[5] { "currentState", "loadingState", "state", "currentLoadingState", "loadState" };
				foreach (string name in array)
				{
					FieldInfo field2 = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field2 != null)
					{
						text = field2.GetValue(obj)?.ToString() ?? "";
						if (!string.IsNullOrEmpty(text))
						{
							break;
						}
					}
				}
				if (!_loadingMonitorActive && !_loadingMonitorDebugLogged)
				{
					Log.LogInfo((object)$"[DirectConnect] LoadingUI found! Active={flag3}, State='{text}'");
					FieldInfo[] fields2 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					Log.LogInfo((object)("[DirectConnect] LoadingUI fields: " + string.Join(", ", fields2.Select((FieldInfo f) => f.Name))));
					_loadingMonitorDebugLogged = true;
				}
				if (!flag3)
				{
					return;
				}
				if (!_loadingMonitorActive)
				{
					_loadingMonitorActive = true;
					_loadingStuckTimer = 0f;
					_lastLoadingState = text;
					Log.LogInfo((object)("[DirectConnect] Loading monitor started at state: '" + text + "'"));
				}
				_loadingStuckTimer += Time.unscaledDeltaTime;
				int num = (int)_loadingStuckTimer;
				if (num > 0 && num != _lastLoggedSecond)
				{
					_lastLoggedSecond = num;
					Log.LogInfo((object)$"[DirectConnect] Loading monitor: {_loadingStuckTimer:F2}s at '{text}' (deltaTime={Time.deltaTime:F4})");
				}
				if (((!NullSafetyPatches._saveLoaded || !(_loadingStuckTimer > 10f)) && !(_loadingStuckTimer > 30f)) || _finishLoadingCalled)
				{
					return;
				}
				_finishLoadingCalled = true;
				Log.LogWarning((object)$"[DirectConnect] Loading stuck for {_loadingStuckTimer:F1}s at '{text}' - forcing completion!");
				MethodInfo methodInfo = AccessTools.Method(type, "OnFinishLoading", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					try
					{
						methodInfo.Invoke(obj, null);
						Log.LogInfo((object)"[DirectConnect] Successfully called LoadingUI.OnFinishLoading()!");
						if (Time.timeScale == 0f)
						{
							Time.timeScale = 1f;
							Log.LogInfo((object)"[DirectConnect] Restored Time.timeScale to 1");
						}
						FieldInfo field3 = type.GetField("confirmedLoad", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (field3 != null)
						{
							field3.SetValue(obj, true);
							Log.LogInfo((object)"[DirectConnect] Set confirmedLoad = true");
						}
						if (field != null)
						{
							field.SetValue(obj, true);
							Log.LogInfo((object)"[DirectConnect] Set _loaded = true");
						}
						FieldInfo field4 = type.GetField("cg", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						if (field4 != null)
						{
							object value = field4.GetValue(obj);
							if (value != null)
							{
								Type type2 = value.GetType();
								type2.GetProperty("alpha")?.SetValue(value, 0f);
								type2.GetProperty("blocksRaycasts")?.SetValue(value, false);
								Log.LogInfo((object)"[DirectConnect] Set CanvasGroup alpha=0, hidden (but gameObject still active)");
							}
						}
						EnsureNetworkMessageRelayExists();
						return;
					}
					catch (Exception ex)
					{
						Log.LogError((object)("[DirectConnect] Error calling OnFinishLoading: " + ex.Message));
						return;
					}
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Log.LogInfo((object)("[DirectConnect] OnFinishLoading not found. Available methods: " + string.Join(", ", methods.Select((MethodInfo m) => m.Name))));
			}
			catch (Exception ex2)
			{
				Log.LogError((object)("[DirectConnect] Loading monitor error: " + ex2.Message));
			}
		}

		private void EnsureNetworkMessageRelayExists()
		{
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("NetworkMessageRelay");
				if (type == null)
				{
					Log.LogWarning((object)"[DirectConnect] NetworkMessageRelay type not found");
					return;
				}
				FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					Log.LogWarning((object)"[DirectConnect] Could not find NetworkMessageRelay.instance field");
					return;
				}
				object value = field.GetValue(null);
				if (value != null)
				{
					object obj = ((value is MonoBehaviour) ? value : null);
					bool flag = (Object)(object)((obj != null) ? ((Component)obj).GetComponent<NetworkIdentity>() : null) != (Object)null;
					Log.LogInfo((object)$"[DirectConnect] NetworkMessageRelay.instance already exists (hasNetworkIdentity={flag})");
					return;
				}
				Object[] array = Object.FindObjectsOfType(type);
				Log.LogInfo((object)$"[DirectConnect] Found {array.Length} NetworkMessageRelay objects in scene");
				Object[] array2 = array;
				foreach (Object val in array2)
				{
					MonoBehaviour val2 = (MonoBehaviour)(object)((val is MonoBehaviour) ? val : null);
					if ((Object)(object)val2 != (Object)null)
					{
						NetworkIdentity component = ((Component)val2).GetComponent<NetworkIdentity>();
						Log.LogInfo((object)$"[DirectConnect] Found relay: {((Object)val2).name}, hasNetworkIdentity={(Object)(object)component != (Object)null}, netId={((component != null) ? component.netId : 0u)}");
						if ((Object)(object)component != (Object)null)
						{
							field.SetValue(null, val);
							Log.LogInfo((object)$"[DirectConnect] Using scene NetworkMessageRelay with netId {component.netId}");
							return;
						}
					}
				}
				Log.LogWarning((object)"[DirectConnect] No networked scene relay found - creating dummy (commands may not work!)");
				GameObject val3 = new GameObject("DirectConnect_DummyNetworkMessageRelay");
				Object.DontDestroyOnLoad((Object)val3);
				Component value2 = val3.AddComponent(type);
				field.SetValue(null, value2);
				Log.LogInfo((object)"[DirectConnect] Created dummy NetworkMessageRelay instance");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("[DirectConnect] Failed to create NetworkMessageRelay: " + ex.Message));
			}
		}

		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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_00c2: 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)
				{
					Log.LogInfo((object)"[DirectConnect] Not in game world - triggering JoinGameAsClient to load game scenes...");
					_statusMessage = "Loading game...";
					_pendingServerAddress = _serverAddress;
					_pendingServerPort = result;
					if (EnableDirectConnect(result))
					{
						NetworkManager singleton = NetworkManager.singleton;
						if ((Object)(object)singleton != (Object)null)
						{
							singleton.networkAddress = _serverAddress;
							Log.LogInfo((object)("[DirectConnect] Set network address to: " + _serverAddress));
						}
						try
						{
							FlowManager.JoinGameAsClient((Action)delegate
							{
								Log.LogInfo((object)"[DirectConnect] JoinGameAsClient callback - scene loaded!");
							});
							Log.LogInfo((object)"[DirectConnect] Called FlowManager.JoinGameAsClient successfully");
							return;
						}
						catch (Exception arg)
						{
							Log.LogError((object)$"[DirectConnect] Failed to call JoinGameAsClient: {arg}");
							_statusMessage = "Failed to load game - try loading a save first";
							return;
						}
					}
					_statusMessage = "Failed to initialize connection";
				}
				else
				{
					DoConnect(_serverAddress, result);
				}
			}
			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 IEnumerator ConnectAfterSceneLoad()
		{
			Log.LogInfo((object)"[DirectConnect] ConnectAfterSceneLoad - waiting for game world...");
			_statusMessage = "Loading game world...";
			float maxWait = 30f;
			float elapsed = 0f;
			while (elapsed < maxWait)
			{
				bool flag = false;
				for (int i = 0; i < SceneManager.sceneCount; i++)
				{
					Scene sceneAt = SceneManager.GetSceneAt(i);
					string name = ((Scene)(ref sceneAt)).name;
					if (name.Contains("Player") || name.Contains("Voxeland"))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					Log.LogInfo((object)"[DirectConnect] Game world loaded! Starting connection...");
					yield return (object)new WaitForSeconds(2f);
					DoConnect(_pendingServerAddress, _pendingServerPort);
					yield break;
				}
				elapsed += 0.5f;
				yield return (object)new WaitForSeconds(0.5f);
			}
			_statusMessage = "Timeout waiting for game to load";
			Log.LogError((object)"[DirectConnect] Timeout waiting for game world to load");
		}

		private IEnumerator WaitForGameWorldAndConnect(int port)
		{
			Log.LogInfo((object)"[DirectConnect] WaitForGameWorldAndConnect - polling for game world...");
			_statusMessage = "Loading game world...";
			_pendingServerAddress = _serverAddress;
			_pendingServerPort = port;
			float maxWait = 60f;
			float elapsed = 0f;
			while (elapsed < maxWait)
			{
				bool flag = false;
				for (int i = 0; i < SceneManager.sceneCount; i++)
				{
					Scene sceneAt = SceneManager.GetSceneAt(i);
					string name = ((Scene)(ref sceneAt)).name;
					Log.LogInfo((object)("[DirectConnect] Checking scene: " + name));
					if (name.Contains("Player") || name.Contains("Voxeland"))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					Log.LogInfo((object)"[DirectConnect] Game world loaded! Starting connection...");
					yield return (object)new WaitForSeconds(3f);
					DoConnect(_pendingServerAddress, _pendingServerPort);
					yield break;
				}
				elapsed += 1f;
				_statusMessage = $"Loading game world... ({elapsed:0}s)";
				yield return (object)new WaitForSeconds(1f);
			}
			_statusMessage = "Timeout waiting for game to load";
			Log.LogError((object)"[DirectConnect] Timeout waiting for game world to load");
		}

		private void DoConnect(string address, int port)
		{
			try
			{
				_isConnecting = true;
				_statusMessage = "Connecting...";
				LastServerAddress.Value = address;
				if (!EnableDirectConnect(port))
				{
					_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;
				}
				string text = address;
				if (!IPAddress.TryParse(address, out IPAddress _))
				{
					try
					{
						_statusMessage = "Resolving " + address + "...";
						Log.LogInfo((object)("[DirectConnect] Resolving hostname: " + address));
						IPAddress[] hostAddresses = Dns.GetHostAddresses(address);
						foreach (IPAddress iPAddress in hostAddresses)
						{
							if (iPAddress.AddressFamily == AddressFamily.InterNetwork)
							{
								text = iPAddress.ToString();
								Log.LogInfo((object)("[DirectConnect] Resolved " + address + " -> " + text));
								break;
							}
						}
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)("[DirectConnect] DNS resolution failed: " + ex.Message + ", trying direct connection..."));
					}
				}
				singleton.networkAddress = text;
				singleton.StartClient();
				Log.LogInfo((object)($"[DirectConnect] Connecting to {text}:{port}..." + ((text != address) ? (" (resolved from " + address + ")") : "")));
				((MonoBehaviour)this).StartCoroutine(CheckConnection());
			}
			catch (Exception ex2)
			{
				_statusMessage = "Error: " + ex2.Message;
				_isConnecting = false;
				Log.LogError((object)$"[DirectConnect] Connection error: {ex2}");
			}
		}

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

		public void ShowConnectUI()
		{
			_showConnectUI = true;
			_statusMessage = "";
			Log.LogInfo((object)"[DirectConnect] Opening connect UI from main menu");
		}

		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 = 30000;
				_kcpTransport.DualMode = false;
				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.103";
	}
	[HarmonyPatch]
	public static class NetworkRelayLinkingPatches
	{
		private static bool _relayLinked;

		private static bool _loggedOnce;

		public static void Reset()
		{
			_relayLinked = false;
			_loggedOnce = false;
			NullSafetyPatches._sendActionLogged = false;
			NullSafetyPatches._serverRelayNetId = 2u;
			NullSafetyPatches._actionCount = 0;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(NetworkClient), "OnSpawn")]
		public static bool OnSpawn_Prefix(SpawnMessage message)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if (message.assetId == Guid.Empty && message.sceneId == 0L)
			{
				if (!_loggedOnce)
				{
					Plugin.Log.LogInfo((object)$"[DirectConnect] Intercepted spawn for netId {message.netId} with no assetId/sceneId - will try to link scene relay");
					_loggedOnce = true;
				}
				if (_relayLinked)
				{
					return false;
				}
				NullSafetyPatches._serverRelayNetId = message.netId;
				Plugin.Log.LogInfo((object)$"[DirectConnect] Server relay netId detected: {message.netId}");
				if (LinkSceneRelayToNetId(message.netId))
				{
					_relayLinked = true;
					return false;
				}
			}
			return true;
		}

		private static bool LinkSceneRelayToNetId(uint targetNetId)
		{
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Expected O, but got Unknown
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Type type = AccessTools.TypeByName("NetworkMessageRelay");
				if (type == null)
				{
					Plugin.Log.LogWarning((object)"[DirectConnect] NetworkMessageRelay type not found");
					return false;
				}
				Object[] array = Object.FindObjectsOfType(type);
				Plugin.Log.LogInfo((object)$"[DirectConnect] Found {array.Length} NetworkMessageRelay objects in scene");
				NetworkIdentity val = null;
				object obj = null;
				Object[] array2 = array;
				foreach (Object val2 in array2)
				{
					MonoBehaviour val3 = (MonoBehaviour)(object)((val2 is MonoBehaviour) ? val2 : null);
					if (!((Object)(object)val3 == (Object)null))
					{
						val = ((Component)val3).GetComponent<NetworkIdentity>();
						if ((Object)(object)val != (Object)null)
						{
							obj = val2;
							Plugin.Log.LogInfo((object)("[DirectConnect] Found scene relay '" + ((Object)val3).name + "' with identity"));
							break;
						}
						Plugin.Log.LogInfo((object)("[DirectConnect] Relay '" + ((Object)val3).name + "' has no NetworkIdentity"));
					}
				}
				if (obj == null || (Object)(object)val == (Object)null)
				{
					Plugin.Log.LogInfo((object)$"[DirectConnect] No scene relay found - creating networked relay for netId {targetNetId}");
					GameObject val4 = new GameObject("DirectConnect_NetworkMessageRelay");
					Object.DontDestroyOnLoad((Object)val4);
					val = val4.AddComponent<NetworkIdentity>();
					obj = val4.AddComponent(type);
					FieldInfo field = typeof(NetworkBehaviour).GetField("<netIdentity>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
					if (field != null)
					{
						field.SetValue(obj, val);
						Plugin.Log.LogInfo((object)"[DirectConnect] Set netIdentity backing field on relay");
					}
					else
					{
						Plugin.Log.LogWarning((object)"[DirectConnect] Could not find netIdentity backing field!");
					}
					PropertyInfo property = typeof(NetworkIdentity).GetProperty("NetworkBehaviours", BindingFlags.Instance | BindingFlags.Public);
					if (property != null)
					{
						MethodInfo setMethod = property.GetSetMethod(nonPublic: true);
						if (setMethod != null)
						{
							NetworkBehaviour[] array3 = (NetworkBehaviour[])(object)new NetworkBehaviour[1] { (NetworkBehaviour)((obj is NetworkBehaviour) ? obj : null) };
							setMethod.Invoke(val, new object[1] { array3 });
							Plugin.Log.LogInfo((object)"[DirectConnect] Set NetworkBehaviours array on identity");
						}
						else
						{
							Plugin.Log.LogWarning((object)"[DirectConnect] Could not get NetworkBehaviours setter!");
						}
					}
					else
					{
						Plugin.Log.LogWarning((object)"[DirectConnect] Could not find NetworkBehaviours property!");
					}
					PropertyInfo property2 = typeof(NetworkBehaviour).GetProperty("ComponentIndex", BindingFlags.Instance | BindingFlags.Public);
					if (property2 != null)
					{
						MethodInfo setMethod2 = property2.GetSetMethod(nonPublic: true);
						if (setMethod2 != null)
						{
							setMethod2.Invoke(obj, new object[1] { (byte)0 });
							Plugin.Log.LogInfo((object)"[DirectConnect] Set ComponentIndex = 0 on relay");
						}
						else
						{
							Plugin.Log.LogWarning((object)"[DirectConnect] Could not get ComponentIndex setter!");
						}
					}
					else
					{
						Plugin.Log.LogWarning((object)"[DirectConnect] Could not find ComponentIndex property!");
					}
					Plugin.Log.LogInfo((object)"[DirectConnect] Created NetworkMessageRelay with NetworkIdentity");
				}
				Plugin.Log.LogInfo((object)$"[DirectConnect] Linking relay to netId {targetNetId}");
				bool flag = false;
				FieldInfo field2 = typeof(NetworkIdentity).GetField("_netId", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field2 == null)
				{
					field2 = typeof(NetworkIdentity).GetField("netId", BindingFlags.Instance | BindingFlags.NonPublic);
				}
				if (field2 != null)
				{
					field2.SetValue(val, targetNetId);
					Plugin.Log.LogInfo((object)$"[DirectConnect] Set identity._netId = {targetNetId} via field");
					flag = true;
				}
				else
				{
					PropertyInfo property3 = typeof(NetworkIdentity).GetProperty("netId", BindingFlags.Instance | BindingFlags.Public);
					if (property3 != null)
					{
						MethodInfo setMethod3 = property3.GetSetMethod(nonPublic: true);
						if (setMethod3 != null)
						{
							setMethod3.Invoke(val, new object[1] { targetNetId });
							Plugin.Log.LogInfo((object)$"[DirectConnect] Set identity.netId = {targetNetId} via property setter");
							flag = true;
						}
					}
				}
				if (!flag)
				{
					Plugin.Log.LogError((object)"[DirectConnect] Could not set netId on identity!");
					return false;
				}
				if (!NetworkClient.spawned.ContainsKey(targetNetId))
				{
					NetworkClient.spawned[targetNetId] = val;
					Plugin.Log.LogInfo((object)$"[DirectConnect] Added identity to NetworkClient.spawned[{targetNetId}]");
				}
				FieldInfo field3 = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field3 != null)
				{
					field3.SetValue(null, obj);
					Plugin.Log.LogInfo((object)"[DirectConnect] Set NetworkMessageRelay.instance");
				}
				FieldInfo field4 = typeof(NetworkIdentity).GetField("isClient", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field4 != null)
				{
					field4.SetValue(val, true);
				}
				FieldInfo field5 = typeof(NetworkIdentity).GetField("_hasAuthority", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field5 != null)
				{
					field5.SetValue(val, true);
					Plugin.Log.LogInfo((object)"[DirectConnect] Set hasAuthority = true");
				}
				PropertyInfo property4 = typeof(NetworkIdentity).GetProperty("connectionToServer", BindingFlags.Instance | BindingFlags.Public);
				if (property4 != null && NetworkClient.connection != null)
				{
					MethodInfo setMethod4 = property4.GetSetMethod(nonPublic: true);
					if (setMethod4 != null)
					{
						setMethod4.Invoke(val, new object[1] { NetworkClient.connection });
						Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer = NetworkClient.connection (via property)");
					}
					else
					{
						try
						{
							property4.SetValue(val, NetworkClient.connection);
							Plugin.Log.LogInfo((object)"[DirectConnect] Set connectionToServer = NetworkClient.connection (direct)");
						}
						catch (Exception ex)
						{
							Plugin.Log.LogWarning((object)("[DirectConnect] Could not set connectionToServer: " + ex.Message));
						}
					}
				}
				else
				{
					Plugin.Log.LogWarning((object)$"[DirectConnect] Could not set connectionToServer: prop={property4 != null}, connection={NetworkClient.connection != null}");
				}
				Plugin.Log.LogInfo((object)$"[DirectConnect] Successfully linked NetworkMessageRelay to server's netId {targetNetId}!");
				return true;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[DirectConnect] Failed to link scene relay: {arg}");
				return false;
			}
		}
	}
	[HarmonyPatch]
	public static class FizzyFacepunchPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(FizzyFacepunch), "ClientEarlyUpdate")]
		public static bool ClientEarlyUpdate_Prefix()
		{
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(FizzyFacepunch), "ClientLateUpdate")]
		public static bool ClientLateUpdate_Prefix()
		{
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(FizzyFacepunch), "ServerEarlyUpdate")]
		public static bool ServerEarlyUpdate_Prefix()
		{
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(FizzyFacepunch), "ServerLateUpdate")]
		public static bool ServerLateUpdate_Prefix()
		{
			return false;
		}
	}
	[HarmonyPatch]
	public static class MainMenuPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(MainMenuUI), "RefreshHiddenButtonState")]
		public static void RefreshHiddenButtonState_Postfix(MainMenuUI __instance)
		{
			try
			{
				if (AccessTools.Field(typeof(MainMenuUI), "menuSelections")?.GetValue(__instance) is MainMenuItem[] array && array.Length > 3)
				{
					((Component)array[3]).gameObject.SetActive(true);
					Plugin.Log.LogInfo((object)"[DirectConnect] Enabled 'Join Multiplayer' button in main menu");
				}
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[DirectConnect] Failed to patch main menu: {arg}");
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(MainMenuUI), "JoinMultiplayerAsClient")]
		public static bool JoinMultiplayerAsClient_Prefix()
		{
			try
			{
				Plugin.Log.LogInfo((object)"[DirectConnect] 'Join Multiplayer' clicked - showing connect dialog");
				Plugin.Instance.ShowConnectUI();
				return false;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"[DirectConnect] Error in JoinMultiplayerAsClient: {arg}");
				return true;
			}
		}
	}
	public static class NullSafetyPatches
	{
		private static bool _patchesApplied = false;

		public static uint _serverRelayNetId = 2u;

		public static bool _sendActionLogged = false;

		public static int _actionCount = 0;

		private static readonly Dictionary<string, string> _actionTypeToCommand = new Dictionary<string, string>
		{
			{ "ConveyorBuildAction", "BuildConveyorCommand" },
			{ "HVCBuildAction", "BuildHvcCommand" },
			{ "MassTransitBuildAction", "BuildMassTransitCommand" },
			{ "MassTransitChainBuildAction", "BuildMassTransitChainCommand" },
			{ "ResearchCoreBuildAction", "BuildResearchCoreCommand" },
			{ "SimpleBuildAction", "BuildSimpleMachineCommand" },
			{ "ExchangeMachineAction", "ExchangeMachineInvCommand" },
			{ "RotateMachineAction", "RotateMachineCommand" },
			{ "SwapVariantAction", "SwapVariantCommand" },
			{ "EraseMachineAction", "EraseMachineCommand" },
			{ "ChangeFilterAction", "SetFilterCommand" },
			{ "HarvestPlantAction", "HarvestPlantCommand" },
			{ "TakeAllAction", "TakeAllCommand" },
			{ "HitDestructibleAction", "HitDestructableCommand" },
			{ "HitVoxelAction", "HitVoxelCommand" },
			{ "RepairAction", "RepairMachineCommand" },
			{ "CheatAction", "CheatCommand" },
			{ "CraftAction", "CraftCommand" },
			{ "ActivatePowerAction", "ActivatePowerCommand" },
			{ "ScanAction", "ScanCommand" },
			{ "UnlockTechAction", "UnlockTechCommand" },
			{ "DeactivateTechAction", "DeactivateTechCommand" },
			{ "ModifyMouseBufferAction", "ModifyMouseBufferCommand" },
			{ "InteractAction", "InteractCommand" },
			{ "ActivateMachineAction", "ActivateMachineCommand" },
			{ "MOLEAction", "MOLECommand" },
			{ "SortInventoryAction", "SortCommand" },
			{ "TempConveyorAction", "UpdateTempConveyorsCommand" },
			{ "UpgradeProductionTerminalAction", "UpgradeResearchCommand" },
			{ "UnlockDatabankEntryAction", "UnlockDatabankEntryCommand" },
			{ "QuestStatusAction", "CompleteQuestCommand" },
			{ "ExplosiveBuildAction", "BuildExplosiveCommand" },
			{ "ExplodeAction", "ExplodeCommand" },
			{ "CompleteResourceGateAction", "CompleteResourceGateCommand" },
			{ "CompleteRepairElevatorAction", "CompleteRepairElevatorCommand" },
			{ "CheatUpgradeAction", "CheatUpgradeCommand" },
			{ "MainReceiverAction", "MainReceiverCommand" },
			{ "BuildWithRecipeAction", "BuildMachineWithRecipeCommand" },
			{ "MultiPlaceBuildAction", "BuildMultiPlaceCommand" },
			{ "ReplaceAction", "ReplaceCommand" },
			{ "ResizeStorageLimitAction", "ResizeStorageLimitCommand" },
			{ "ShootAction", "ShootCommand" },
			{ "MultiplayerGameAction", "MultiplayerGameCommand" },
			{ "SetNexusTeamAction", "SetNexusTeamCommand" },
			{ "MultiplayerGameLootNexusAction", "MultiplayerGameLootNexusCommand" },
			{ "AsteroidsAction", "AsteroidsCommand" },
			{ "PingAction", "PingCommand" },
			{ "OmniseekablePingAction", "OmniseekablePingCommand" },
			{ "SandPumpBuildAction", "BuildSandPumpCommand" }
		};

		private static int _sendCommandCount = 0;

		private static bool _requestSimTickHandled = false;

		public static bool _saveLoaded = false;

		public static void ApplyPatches(Harmony harmony)
		{
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c1: Expected O, but got Unknown
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d8: Expected O, but got Unknown
			//IL_04a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Expected O, but got Unknown
			//IL_0520: Unknown result type (might be due to invalid IL or missing references)
			//IL_0527: Expected O, but got Unknown
			//IL_05aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b1: Expected O, but got Unknown
			//IL_0623: Unknown result type (might be due to invalid IL or missing references)
			//IL_062a: Expected O, but got Unknown
			//IL_069c: Unknown result type (might be due to invalid IL or missing references)
			//IL_06a3: Expected O, but got Unknown
			//IL_0715: Unknown result type (might be due to invalid IL or missing references)
			//IL_071c: Expected O, but got Unknown
			//IL_078e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0795: Expected O, but got Unknown
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_03d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03de: Expected O, but got Unknown
			//IL_0382: Unknown result type (might be due to invalid IL or missing references)
			//IL_0389: Expected O, but got Unknown
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			//IL_0327: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Expected O, but got Unknown
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0246: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Expected O, but got Unknown
			if (_patchesApplied)
			{
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("ThirdPersonDisplayAnimator");
				if (type != null)
				{
					MethodInfo methodInfo = AccessTools.Method(type, "Update", (Type[])null, (Type[])null);
					if (methodInfo != null)
					{
						HarmonyMethod val = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched ThirdPersonDisplayAnimator.Update to skip");
					}
					MethodInfo methodInfo2 = AccessTools.Method(type, "UpdateSillyStuff", (Type[])null, (Type[])null);
					if (methodInfo2 != null)
					{
						HarmonyMethod val2 = new HarmonyMethod(typeof(NullSafetyPatches), "Skip_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo2, val2, (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 type2 = AccessTools.TypeByName("NetworkMessageRelay");
				if (type2 != null)
				{
					MethodInfo methodInfo3 = AccessTools.Method(type2, "SendNetworkAction", (Type[])null, (Type[])null);
					if (methodInfo3 != null)
					{
						HarmonyMethod val3 = new HarmonyMethod(typeof(NullSafetyPatches), "SendNetworkAction_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo3, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.SendNetworkAction (complete replacement)");
					}
					MethodInfo methodInfo4 = AccessTools.Method(type2, "CmdSendNetworkAction", (Type[])null, (Type[])null);
					if (methodInfo4 != null)
					{
						HarmonyMethod val4 = new HarmonyMethod(typeof(NullSafetyPatches), "CmdSendNetworkAction_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo4, val4, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.CmdSendNetworkAction");
					}
					else
					{
						Plugin.Log.LogWarning((object)"[DirectConnect] CmdSendNetworkAction method not found - checking for other command names");
						MethodInfo[] methods = type2.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
						foreach (MethodInfo methodInfo5 in methods)
						{
							if (methodInfo5.Name.Contains("Cmd") || methodInfo5.Name.Contains("Command") || methodInfo5.Name.Contains("Send"))
							{
								Plugin.Log.LogInfo((object)("[DirectConnect] Found method: " + methodInfo5.Name));
							}
						}
					}
				}
				try
				{
					MethodInfo method = typeof(NetworkBehaviour).GetMethod("SendCommandInternal", BindingFlags.Instance | BindingFlags.NonPublic);
					if (method != null)
					{
						HarmonyMethod val5 = new HarmonyMethod(typeof(NullSafetyPatches), "SendCommandInternal_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method, val5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched Mirror.NetworkBehaviour.SendCommandInternal");
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch SendCommandInternal (non-critical): " + ex.Message));
				}
				if (type2 != null)
				{
					MethodInfo methodInfo6 = AccessTools.Method(type2, "RequestCurrentSimTick", (Type[])null, (Type[])null);
					if (methodInfo6 != null)
					{
						HarmonyMethod val6 = new HarmonyMethod(typeof(NullSafetyPatches), "RequestCurrentSimTick_Prefix", (Type[])null);
						HarmonyMethod val7 = new HarmonyMethod(typeof(NullSafetyPatches), "RequestCurrentSimTick_Finalizer", (Type[])null);
						harmony.Patch((MethodBase)methodInfo6, val6, (HarmonyMethod)null, (HarmonyMethod)null, val7, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.RequestCurrentSimTick with prefix and finalizer");
					}
					MethodInfo methodInfo7 = AccessTools.Method(type2, "UserCode_ProcessCurrentSimTick", (Type[])null, (Type[])null);
					if (methodInfo7 != null)
					{
						HarmonyMethod val8 = new HarmonyMethod(typeof(NullSafetyPatches), "ProcessCurrentSimTick_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo7, val8, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkMessageRelay.UserCode_ProcessCurrentSimTick with prefix");
					}
				}
				Type type3 = AccessTools.TypeByName("NetworkedPlayer");
				if (type3 != null)
				{
					MethodInfo methodInfo8 = AccessTools.Method(type3, "OnStopClient", (Type[])null, (Type[])null);
					if (methodInfo8 != null)
					{
						HarmonyMethod val9 = new HarmonyMethod(typeof(NullSafetyPatches), "OnStopClient_Prefix", (Type[])null);
						harmony.Patch((MethodBase)methodInfo8, val9, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkedPlayer.OnStopClient for disconnect tracking");
					}
				}
				MethodInfo methodInfo9 = AccessTools.Method(typeof(NetworkManager), "OnClientDisconnect", (Type[])null, (Type[])null);
				if (methodInfo9 != null)
				{
					HarmonyMethod val10 = new HarmonyMethod(typeof(NullSafetyPatches), "OnClientDisconnect_Prefix", (Type[])null);
					harmony.Patch((MethodBase)methodInfo9, val10, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkManager.OnClientDisconnect for disconnect tracking");
				}
				try
				{
					MethodInfo method2 = typeof(NetworkClient).GetMethod("OnObjectSpawn", BindingFlags.Static | BindingFlags.NonPublic);
					if (method2 != null)
					{
						HarmonyMethod val11 = new HarmonyMethod(typeof(NullSafetyPatches), "OnObjectSpawn_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method2, val11, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.OnObjectSpawn for spawn tracking");
					}
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch OnObjectSpawn: " + ex2.Message));
				}
				try
				{
					MethodInfo method3 = typeof(NetworkClient).GetMethod("OnObjectDestroy", BindingFlags.Static | BindingFlags.NonPublic);
					if (method3 != null)
					{
						HarmonyMethod val12 = new HarmonyMethod(typeof(NullSafetyPatches), "OnObjectDestroy_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method3, val12, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.OnObjectDestroy for destroy tracking");
					}
				}
				catch (Exception ex3)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch OnObjectDestroy: " + ex3.Message));
				}
				try
				{
					MethodInfo method4 = typeof(NetworkIdentity).GetMethod("HandleRemoteCall", BindingFlags.Instance | BindingFlags.NonPublic);
					if (method4 != null)
					{
						HarmonyMethod val13 = new HarmonyMethod(typeof(NullSafetyPatches), "HandleRemoteCall_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method4, val13, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkIdentity.HandleRemoteCall for RPC tracking");
					}
				}
				catch (Exception ex4)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch HandleRemoteCall: " + ex4.Message));
				}
				try
				{
					MethodInfo method5 = typeof(NetworkClient).GetMethod("Connect", new Type[1] { typeof(string) });
					if (method5 != null)
					{
						HarmonyMethod val14 = new HarmonyMethod(typeof(NullSafetyPatches), "NetworkClient_Connect_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method5, val14, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched NetworkClient.Connect");
					}
				}
				catch (Exception ex5)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch NetworkClient.Connect: " + ex5.Message));
				}
				try
				{
					MethodInfo method6 = typeof(KcpTransport).GetMethod("OnClientConnected", BindingFlags.Instance | BindingFlags.NonPublic);
					if (method6 != null)
					{
						HarmonyMethod val15 = new HarmonyMethod(typeof(NullSafetyPatches), "KCP_OnClientConnected_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method6, val15, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpTransport.OnClientConnected");
					}
				}
				catch (Exception ex6)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpTransport.OnClientConnected: " + ex6.Message));
				}
				try
				{
					MethodInfo method7 = typeof(KcpTransport).GetMethod("OnClientDisconnected", BindingFlags.Instance | BindingFlags.NonPublic);
					if (method7 != null)
					{
						HarmonyMethod val16 = new HarmonyMethod(typeof(NullSafetyPatches), "KCP_OnClientDisconnected_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method7, val16, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpTransport.OnClientDisconnected");
					}
				}
				catch (Exception ex7)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpTransport.OnClientDisconnected: " + ex7.Message));
				}
				try
				{
					MethodInfo method8 = typeof(KcpClient).GetMethod("Disconnect", BindingFlags.Instance | BindingFlags.Public);
					if (method8 != null)
					{
						HarmonyMethod val17 = new HarmonyMethod(typeof(NullSafetyPatches), "KcpClient_Disconnect_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method8, val17, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpClient.Disconnect");
					}
				}
				catch (Exception ex8)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpClient.Disconnect: " + ex8.Message));
				}
				try
				{
					MethodInfo method9 = typeof(KcpConnection).GetMethod("Disconnect", BindingFlags.Instance | BindingFlags.Public);
					if (method9 != null)
					{
						HarmonyMethod val18 = new HarmonyMethod(typeof(NullSafetyPatches), "KcpConnection_Disconnect_Prefix", (Type[])null);
						harmony.Patch((MethodBase)method9, val18, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Plugin.Log.LogInfo((object)"[DirectConnect] Patched KcpConnection.Disconnect");
					}
				}
				catch (Exception ex9)
				{
					Plugin.Log.LogWarning((object)("[DirectConnect] Failed to patch KcpConnection.Disconnect: " + ex9.Message));
				}
				_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 bool SendNetworkAction_Prefix(object __instance, object action)
		{
			_actionCount++;
			string text = action?.GetType()?.Name ?? "null";
			try
			{
				Plugin.Log.LogInfo((object)$"[DirectConnect] SendNetworkAction #{_actionCount}: {text}");
				object obj = ((__instance is NetworkBehaviour) ? __instance : null);
				if ((Object)(object)((obj != null) ? ((NetworkBehaviour)obj).netIdentity : null) == (Object)null)
				{
					Plugin.Log.LogError((object)("[DirectConnect] SendNetworkAction: netIdentity is NULL! Cannot send " + text));
					return false;
				}
				if (!_actionTypeToCommand.TryGetValue(text, out var value))
				{
					Plugin.Log.LogError((object)("[DirectConnect] Unknown action type: " + text + ". Cannot send to server!"));
					return false;
				}
				if (SendCommandDirectly(__instance, action, value, text))
				{
					Plugin.Log.LogInfo((object)("[DirectConnect] " + value + " sent successfully via SendCommandInternal!"));
				}
				else
				{
					Plugin.Log.LogError((object)("[DirectConnect] Failed to send " + value + "!"));
				}
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("[DirectConnect] SendNetworkAction_Prefix error for " + text + ": " + ex.Message));
				Plugin.Log.LogError((object)("[DirectConnect] Stack: " + ex.StackTrace));
				return false;
			}
		}

		private static bool SendCommandDirectly(object relayInstance, object action, string commandName, string actionTypeName)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NetworkBehaviour val = (Netwo

plugins/kcp2k.dll

Decompiled 7 months 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);
	}
}