Decompiled source of HowToFishDirectConnect v1.3.0

BepInEx/plugins/HowToFishDirect.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Logging;
using FishNet.Managing.Server;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using FishNet.Transporting.Multipass;
using FishNet.Transporting.UTP;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Steamworks;
using UnityEngine;
using UnityEngine.SceneManagement;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HowToFishDirect
{
	public struct ClientHelloBroadcast : IBroadcast
	{
		public int Protocol;

		public ulong SteamId;

		public string InstallId;

		public string GameVersion;

		public string ModVersion;
	}
	public struct SessionInfoBroadcast : IBroadcast
	{
		public int Seed;

		public string HostGameVersion;

		public string HostModVersion;
	}
	public struct SessionRejectBroadcast : IBroadcast
	{
		public byte ReasonCode;

		public string HostVersion;

		public string ClientVersion;

		public string Reason;
	}
	public static class DirectNet
	{
		private sealed class Identity
		{
			public ulong SteamId;

			public string InstallId;
		}

		private static readonly Dictionary<int, Identity> ClientIdentities = new Dictionary<int, Identity>();

		private static readonly HashSet<ulong> UsedSteamIds = new HashSet<ulong>();

		private static readonly HashSet<ulong> UsedIdentities = new HashSet<ulong>();

		private static NetworkManager _networkManager;

		private static bool _registered;

		private static PropertyInfo _seedProperty;

		private static bool _localClientStarted;

		private static bool _helloAcked;

		private static int _helloAttempts;

		private static float _lastHelloSent;

		private const int MaxHelloAttempts = 8;

		private const float HelloRetryInterval = 0.75f;

		private static byte _rejectCode;

		private static string _rejectHost = "";

		private static string _rejectClient = "";

		private static string _rejectText = "";

		public static bool DirectSessionActive { get; internal set; }

		public static string LastRejectReason
		{
			get
			{
				if (_rejectCode == 1)
				{
					return Loc.T("游戏版本不一致:主机 " + _rejectHost + ",本机 " + _rejectClient, "Game version mismatch: host " + _rejectHost + ", you " + _rejectClient);
				}
				if (_rejectCode == 2)
				{
					return Loc.T("模组协议不兼容(主机 " + _rejectHost + ",本机 " + _rejectClient + "),请双方更新到同一版本", "Incompatible mod protocol (host " + _rejectHost + ", you " + _rejectClient + "); please update to the same version");
				}
				return _rejectText;
			}
		}

		public static void RegisterSerializers()
		{
			GenericWriter<ClientHelloBroadcast>.SetWrite((Action<Writer, ClientHelloBroadcast>)delegate(Writer w, ClientHelloBroadcast v)
			{
				w.WriteInt32(v.Protocol);
				w.WriteUInt64(v.SteamId);
				w.WriteString(v.InstallId ?? "");
				w.WriteString(v.GameVersion ?? "");
				w.WriteString(v.ModVersion ?? "");
			});
			GenericReader<ClientHelloBroadcast>.SetRead((Func<Reader, ClientHelloBroadcast>)delegate(Reader r)
			{
				ClientHelloBroadcast result = new ClientHelloBroadcast
				{
					Protocol = r.ReadInt32()
				};
				if (result.Protocol == 1)
				{
					result.SteamId = r.ReadUInt64();
					result.InstallId = r.ReadStringAllocated();
					result.GameVersion = r.ReadStringAllocated();
					result.ModVersion = r.ReadStringAllocated();
				}
				else
				{
					r.Skip(r.Remaining);
				}
				return result;
			});
			GenericWriter<SessionInfoBroadcast>.SetWrite((Action<Writer, SessionInfoBroadcast>)delegate(Writer w, SessionInfoBroadcast v)
			{
				w.WriteInt32(v.Seed);
				w.WriteString(v.HostGameVersion ?? "");
				w.WriteString(v.HostModVersion ?? "");
			});
			GenericReader<SessionInfoBroadcast>.SetRead((Func<Reader, SessionInfoBroadcast>)((Reader r) => new SessionInfoBroadcast
			{
				Seed = r.ReadInt32(),
				HostGameVersion = r.ReadStringAllocated(),
				HostModVersion = r.ReadStringAllocated()
			}));
			GenericWriter<SessionRejectBroadcast>.SetWrite((Action<Writer, SessionRejectBroadcast>)delegate(Writer w, SessionRejectBroadcast v)
			{
				w.WriteUInt8Unpacked(v.ReasonCode);
				w.WriteString(v.HostVersion ?? "");
				w.WriteString(v.ClientVersion ?? "");
				w.WriteString(v.Reason ?? "");
			});
			GenericReader<SessionRejectBroadcast>.SetRead((Func<Reader, SessionRejectBroadcast>)((Reader r) => new SessionRejectBroadcast
			{
				ReasonCode = r.ReadUInt8Unpacked(),
				HostVersion = r.ReadStringAllocated(),
				ClientVersion = r.ReadStringAllocated(),
				Reason = r.ReadStringAllocated()
			}));
		}

		public static void Register(NetworkManager nm)
		{
			if ((Object)(object)nm == (Object)null || (_networkManager == nm && _registered))
			{
				return;
			}
			if ((Object)(object)_networkManager != (Object)null && _networkManager != nm)
			{
				try
				{
					_networkManager.ClientManager.OnClientConnectionState -= OnClientConnectionState;
					_networkManager.ServerManager.OnRemoteConnectionState -= OnRemoteConnectionState;
				}
				catch (Exception ex)
				{
					Plugin.LogVerbose("Detach old NetworkManager: " + ex.Message);
				}
			}
			_networkManager = nm;
			try
			{
				nm.ServerManager.RegisterBroadcast<ClientHelloBroadcast>((Action<NetworkConnection, ClientHelloBroadcast, Channel>)OnClientHello, false);
				nm.ClientManager.RegisterBroadcast<SessionInfoBroadcast>((Action<SessionInfoBroadcast, Channel>)OnSessionInfo);
				nm.ClientManager.RegisterBroadcast<SessionRejectBroadcast>((Action<SessionRejectBroadcast, Channel>)OnSessionReject);
				nm.ClientManager.OnClientConnectionState -= OnClientConnectionState;
				nm.ClientManager.OnClientConnectionState += OnClientConnectionState;
				nm.ServerManager.OnRemoteConnectionState -= OnRemoteConnectionState;
				nm.ServerManager.OnRemoteConnectionState += OnRemoteConnectionState;
				_registered = true;
				Plugin.LogInfo("Direct broadcast handlers registered.");
			}
			catch (Exception ex2)
			{
				_registered = false;
				Plugin.LogError("Register broadcast handlers failed: " + ex2);
			}
		}

		public static void Shutdown()
		{
			try
			{
				if ((Object)(object)_networkManager != (Object)null)
				{
					_networkManager.ClientManager.OnClientConnectionState -= OnClientConnectionState;
					_networkManager.ServerManager.OnRemoteConnectionState -= OnRemoteConnectionState;
				}
			}
			catch
			{
			}
			_registered = false;
			_networkManager = null;
			Clear();
		}

		private static void OnClientConnectionState(ClientConnectionStateArgs args)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			if ((int)args.ConnectionState == 1)
			{
				_localClientStarted = false;
				_helloAcked = false;
				_helloAttempts = 0;
				if (DirectSessionActive)
				{
					Plugin.LogInfo("Direct session ended (local client stopped).");
					DirectSessionActive = false;
				}
			}
			else if ((int)args.ConnectionState == 8)
			{
				_localClientStarted = true;
				_helloAcked = false;
				_helloAttempts = 0;
				SendHello();
			}
		}

		private static void SendHello()
		{
			if (!DirectSessionActive)
			{
				return;
			}
			try
			{
				ulong num = LocalSteamId();
				ClientHelloBroadcast clientHelloBroadcast = new ClientHelloBroadcast
				{
					SteamId = num,
					InstallId = Plugin.InstallId,
					GameVersion = (Application.version ?? ""),
					ModVersion = "1.3.0",
					Protocol = 1
				};
				_networkManager.ClientManager.Broadcast<ClientHelloBroadcast>(clientHelloBroadcast, (Channel)0);
				_helloAttempts++;
				_lastHelloSent = Time.unscaledTime;
				Plugin.LogInfo($"Sent client hello #{_helloAttempts} (SteamID {num}, install {Short(Plugin.InstallId)}, game {clientHelloBroadcast.GameVersion}, mod {clientHelloBroadcast.ModVersion}).");
			}
			catch (Exception ex)
			{
				Plugin.LogError("Failed to send client hello: " + ex);
			}
		}

		public static void TickHandshake()
		{
			if (_localClientStarted && !_helloAcked && _helloAttempts < 8 && !(Time.unscaledTime - _lastHelloSent < 0.75f))
			{
				SendHello();
			}
		}

		private static void OnRemoteConnectionState(NetworkConnection conn, RemoteConnectionStateArgs args)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (conn == (NetworkConnection)null || (int)args.ConnectionState != 0)
			{
				return;
			}
			if (ClientIdentities.TryGetValue(conn.ClientId, out var value))
			{
				ClientIdentities.Remove(conn.ClientId);
				if (value.SteamId != 0L)
				{
					UsedSteamIds.Remove(value.SteamId);
					UsedIdentities.Remove(value.SteamId);
				}
			}
			Patches.RemovePending(conn.ClientId);
			try
			{
				if ((Object)(object)Server.Instance != (Object)null && ((NetworkBehaviour)Server.Instance).IsServerInitialized)
				{
					SaveManager.SaveServer(true);
					Plugin.LogInfo("Saved host world after a client disconnected.");
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("Save after disconnect failed: " + ex.Message);
			}
		}

		private static void OnClientHello(NetworkConnection conn, ClientHelloBroadcast msg, Channel channel)
		{
			if (!DirectSessionActive)
			{
				return;
			}
			try
			{
				if (conn == (NetworkConnection)null)
				{
					return;
				}
				string text = Application.version ?? "";
				if (msg.Protocol != 1)
				{
					Plugin.LogWarn($"Rejecting client {conn.ClientId}: mod protocol {msg.Protocol} vs host {1}.");
					try
					{
						_networkManager.ServerManager.Broadcast<SessionRejectBroadcast>(conn, new SessionRejectBroadcast
						{
							ReasonCode = 2,
							HostVersion = 1.ToString(),
							ClientVersion = msg.Protocol.ToString()
						}, false, (Channel)0);
						conn.Kick((KickReason)6, (LoggingType)2, "Direct connect protocol mismatch.");
						return;
					}
					catch (Exception ex)
					{
						Plugin.LogWarn("Kick failed: " + ex.Message);
						return;
					}
				}
				if (ClientIdentities.ContainsKey(conn.ClientId))
				{
					Plugin.LogVerbose($"Duplicate hello from client {conn.ClientId}; re-sending session info.");
					if (Patches.HasPendingSpawn(conn.ClientId))
					{
						Patches.OnIdentityReady(conn);
					}
					try
					{
						SendSessionInfo(conn);
						return;
					}
					catch (Exception ex2)
					{
						Plugin.LogWarn("Failed to re-send session info: " + ex2.Message);
						return;
					}
				}
				if (!string.Equals(msg.GameVersion, text, StringComparison.Ordinal))
				{
					Plugin.LogWarn(string.Format("Rejecting client {0}: game {1} vs host {2} (peer mod {3}, host mod {4}).", conn.ClientId, msg.GameVersion, text, msg.ModVersion, "1.3.0"));
					try
					{
						_networkManager.ServerManager.Broadcast<SessionRejectBroadcast>(conn, new SessionRejectBroadcast
						{
							ReasonCode = 1,
							HostVersion = text,
							ClientVersion = msg.GameVersion
						}, false, (Channel)0);
						conn.Kick((KickReason)6, (LoggingType)2, "Direct connect game version mismatch (peer " + msg.GameVersion + " vs host " + text + ").");
						return;
					}
					catch (Exception ex3)
					{
						Plugin.LogWarn("Kick failed: " + ex3.Message);
						return;
					}
				}
				if (!string.Equals(msg.ModVersion, "1.3.0", StringComparison.Ordinal))
				{
					Plugin.LogInfo(string.Format("Client {0} is running mod {1} (host {2}); accepted (mod versions do not need to match).", conn.ClientId, msg.ModVersion, "1.3.0"));
				}
				if (conn.IsLocalClient)
				{
					ulong num = ResolveSteamId(conn);
					ClientIdentities[conn.ClientId] = new Identity
					{
						SteamId = num,
						InstallId = msg.InstallId
					};
					Plugin.LogInfo($"Host local client identity {num}.");
					Patches.OnIdentityReady(conn);
					try
					{
						SendSessionInfo(conn);
						return;
					}
					catch (Exception ex4)
					{
						Plugin.LogWarn("Failed to send session info: " + ex4.Message);
						return;
					}
				}
				ulong num2;
				if (msg.SteamId != 0L && UsedSteamIds.Add(msg.SteamId))
				{
					num2 = msg.SteamId;
				}
				else
				{
					num2 = SyntheticFromInstall(msg.InstallId);
					if (msg.SteamId != 0L)
					{
						Plugin.LogWarn($"Client {conn.ClientId} reports SteamID {msg.SteamId} which is already in use; using install-based id {num2}.");
					}
					else
					{
						Plugin.LogWarn($"Client {conn.ClientId} reported no SteamID; using install-based id {num2}.");
					}
				}
				if (!UsedIdentities.Add(num2))
				{
					ulong num3 = SyntheticFromInstall(msg.InstallId + "#" + conn.ClientId);
					int num4 = 1;
					while (!UsedIdentities.Add(num3) && num4 <= 1000)
					{
						num3 = SyntheticFromInstall(msg.InstallId + "#" + conn.ClientId + "#" + num4);
						num4++;
					}
					Plugin.LogWarn($"Identity {num2} is already in use; assigning {num3} to client {conn.ClientId} instead.");
					num2 = num3;
				}
				ClientIdentities[conn.ClientId] = new Identity
				{
					SteamId = num2,
					InstallId = msg.InstallId
				};
				Plugin.LogInfo($"Host accepted client {conn.ClientId}: SteamID {msg.SteamId} -> identity {num2}.");
				try
				{
					SendSessionInfo(conn);
				}
				catch (Exception ex5)
				{
					Plugin.LogWarn("Failed to send session info: " + ex5.Message);
				}
				Patches.OnIdentityReady(conn);
			}
			catch (Exception ex6)
			{
				Plugin.LogError("OnClientHello failed: " + ex6);
			}
		}

		private static void SendSessionInfo(NetworkConnection conn)
		{
			_networkManager.ServerManager.Broadcast<SessionInfoBroadcast>(conn, new SessionInfoBroadcast
			{
				Seed = GameInfo.Seed,
				HostGameVersion = (Application.version ?? ""),
				HostModVersion = "1.3.0"
			}, false, (Channel)0);
		}

		private static void OnSessionInfo(SessionInfoBroadcast msg, Channel channel)
		{
			try
			{
				_helloAcked = true;
				SetSeed(msg.Seed);
				Plugin.LogInfo($"Received session info from host (seed {msg.Seed}, game {msg.HostGameVersion}, mod {msg.HostModVersion}).");
			}
			catch (Exception ex)
			{
				Plugin.LogError("OnSessionInfo failed: " + ex);
			}
		}

		private static void OnSessionReject(SessionRejectBroadcast msg, Channel channel)
		{
			_helloAcked = true;
			_rejectCode = msg.ReasonCode;
			_rejectHost = msg.HostVersion ?? "";
			_rejectClient = msg.ClientVersion ?? "";
			_rejectText = msg.Reason ?? "";
			Plugin.LogWarn("Host rejected the connection: " + LastRejectReason);
		}

		private static void SetSeed(int seed)
		{
			try
			{
				if (_seedProperty == null)
				{
					_seedProperty = typeof(GameInfo).GetProperty("Seed", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if (_seedProperty == null || !_seedProperty.CanWrite)
				{
					Plugin.LogWarn("GameInfo.Seed is not writable; seed not synced.");
				}
				else
				{
					_seedProperty.SetValue(null, seed, null);
				}
			}
			catch (Exception ex)
			{
				Plugin.LogWarn("Failed to set GameInfo.Seed: " + ex.Message);
			}
		}

		public static bool HasIdentity(NetworkConnection conn)
		{
			if (conn != (NetworkConnection)null)
			{
				return ClientIdentities.ContainsKey(conn.ClientId);
			}
			return false;
		}

		public static ulong ResolveSteamId(NetworkConnection conn)
		{
			if (conn == (NetworkConnection)null)
			{
				ulong num = LocalSteamId();
				if (num == 0L)
				{
					return SyntheticFromInstall(Plugin.InstallId);
				}
				return num;
			}
			if (conn.IsLocalClient)
			{
				ulong num2 = LocalSteamId();
				if (num2 == 0L)
				{
					return SyntheticFromInstall(Plugin.InstallId);
				}
				return num2;
			}
			if (ClientIdentities.TryGetValue(conn.ClientId, out var value) && value.SteamId != 0L)
			{
				return value.SteamId;
			}
			return SyntheticFromInstall(Plugin.InstallId + ":pending:" + conn.ClientId);
		}

		public static ulong SyntheticFromInstall(string installId)
		{
			ulong num = Fnv1a64(installId ?? "");
			return 0x1000000000000000L | (num & 0xFFFFFFFFFFFFFFFL);
		}

		public static void ReserveHostIdentity()
		{
			try
			{
				ulong num = LocalSteamId();
				if (num != 0L)
				{
					UsedSteamIds.Add(num);
					UsedIdentities.Add(num);
				}
				UsedIdentities.Add(SyntheticFromInstall(Plugin.InstallId));
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("ReserveHostIdentity failed: " + ex.Message);
			}
		}

		private static ulong Fnv1a64(string s)
		{
			ulong num = 14695981039346656037uL;
			foreach (char c in s)
			{
				num ^= c;
				num *= 1099511628211L;
			}
			return num;
		}

		private static ulong LocalSteamId()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				return SteamUser.GetSteamID().m_SteamID;
			}
			catch
			{
				return 0uL;
			}
		}

		private static string Short(string s)
		{
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			if (s.Length > 8)
			{
				return s.Substring(0, 8);
			}
			return s;
		}

		public static void Clear()
		{
			ClientIdentities.Clear();
			UsedSteamIds.Clear();
			UsedIdentities.Clear();
			_localClientStarted = false;
			_helloAcked = false;
			_helloAttempts = 0;
			DirectSessionActive = false;
		}

		public static void ResetRejectReason()
		{
			_rejectCode = 0;
			_rejectHost = "";
			_rejectClient = "";
			_rejectText = "";
		}
	}
	public class DirectOverlay : MonoBehaviour
	{
		private enum Page
		{
			Home,
			Host,
			Join,
			Config
		}

		private const int MaxHistory = 8;

		private const float TopBarH = 58f;

		private const float TransitionTime = 0.18f;

		private UiTheme _ui;

		private int _uiFontSize = -1;

		private Rect _window = new Rect(20f, 20f, 560f, 664f);

		private bool _positionInitialized;

		private bool _visible = true;

		private bool _mouseInWindow;

		private float _drawW = 560f;

		private float _drawH = 664f;

		private Page _page;

		private float _pageAnim = 1f;

		private string _port = "7777";

		private int _maxPlayers = 8;

		private string _joinIp = "127.0.0.1";

		private string _joinPort = "7777";

		private Vector2 _scroll;

		private readonly Dictionary<string, float> _hover = new Dictionary<string, float>();

		private float _pulse;

		private float _toastAge = 99f;

		private string _toastText = "";

		private int _toastKind;

		private string _lastStatusSeen = "";

		private readonly List<string> _ipLabels = new List<string>();

		private readonly List<string> _ipValues = new List<string>();

		private float _ipRefreshTimer;

		private bool _saveListLoaded;

		private string _selectedSaveName = "";

		private readonly List<string> _history = new List<string>();

		private string _pendingHistoryIp;

		private int _pendingHistoryPort;

		private bool _awaitingKey;

		private string _hotKey;

		private bool _lastChinese;

		private Coroutine _watchdog;

		private static readonly string[] VirtualHints = new string[15]
		{
			"radmin", "zerotier", "tailscale", "hamachi", "vpn", "tap-", "tap ", "wsl", "hyper-v", "vethernet",
			"virtualbox", "vmware", "docker", "loopback", "虚拟"
		};

		private static bool InMenu
		{
			get
			{
				try
				{
					return MainMenuManager.IsInMenu;
				}
				catch
				{
					return false;
				}
			}
		}

		private void Awake()
		{
			try
			{
				if (Plugin.CfgPort != null)
				{
					_port = Plugin.CfgPort.Value.ToString();
				}
				if (Plugin.CfgMaxPlayers != null)
				{
					_maxPlayers = Plugin.CfgMaxPlayers.Value;
				}
				if (Plugin.CfgJoinIp != null)
				{
					_joinIp = Plugin.CfgJoinIp.Value;
				}
				if (Plugin.CfgShowOverlay != null)
				{
					_visible = Plugin.CfgShowOverlay.Value;
				}
			}
			catch
			{
			}
			LoadHistory();
			RefreshLocalIps();
			_lastChinese = Loc.Chinese;
		}

		private void OnDestroy()
		{
			StopWatchdog();
			try
			{
				_ui?.Dispose();
			}
			catch
			{
			}
			_ui = null;
		}

		private void Update()
		{
			_pulse += Time.unscaledDeltaTime;
			bool chinese = Loc.Chinese;
			if (chinese != _lastChinese)
			{
				_lastChinese = chinese;
				_toastText = "";
				_toastAge = 99f;
			}
			_ipRefreshTimer += Time.unscaledDeltaTime;
			if (_ipRefreshTimer >= 3f)
			{
				_ipRefreshTimer = 0f;
				RefreshLocalIps();
			}
			_pageAnim = Mathf.MoveTowards(_pageAnim, 1f, Time.unscaledDeltaTime / 0.18f);
			if (_toastAge < 99f)
			{
				_toastAge += Time.unscaledDeltaTime;
			}
			WatchStatus();
		}

		private void WatchStatus()
		{
			string lastStatus = DirectTransport.LastStatus;
			if (!string.Equals(lastStatus, _lastStatusSeen, StringComparison.Ordinal))
			{
				_lastStatusSeen = lastStatus;
				if (!string.IsNullOrEmpty(lastStatus))
				{
					ShowToast(lastStatus, ToastKind(lastStatus));
				}
			}
		}

		private static int ToastKind(string s)
		{
			if (Contains(s, "错误") || Contains(s, "失败") || Contains(s, "异常") || Contains(s, "超时") || Contains(s, "拒绝") || Contains(s, "error", ignoreCase: true) || Contains(s, "fail", ignoreCase: true) || Contains(s, "exception", ignoreCase: true) || Contains(s, "reject", ignoreCase: true) || Contains(s, "timeout", ignoreCase: true) || Contains(s, "invalid", ignoreCase: true) || Contains(s, "not found", ignoreCase: true))
			{
				return 2;
			}
			if (Contains(s, "已") || Contains(s, "copied", ignoreCase: true) || Contains(s, "hosting", ignoreCase: true) || Contains(s, "filled", ignoreCase: true) || Contains(s, "restored", ignoreCase: true))
			{
				return 1;
			}
			return 0;
		}

		private static bool Contains(string s, string sub, bool ignoreCase = false)
		{
			if (string.IsNullOrEmpty(s))
			{
				return false;
			}
			return s.IndexOf(sub, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal) >= 0;
		}

		private void OnGUI()
		{
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: 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_020e: Expected O, but got Unknown
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				EnsureUi();
				HandleKeyCapture();
				HandleToggleKey();
				if (!_visible)
				{
					_hotKey = null;
					return;
				}
				if (!_positionInitialized)
				{
					_positionInitialized = true;
					try
					{
						float num = Mathf.Min(((Rect)(ref _window)).width, Mathf.Max(1f, (float)Screen.width - 20f));
						float num2 = Mathf.Min(((Rect)(ref _window)).height, Mathf.Max(1f, (float)Screen.height - 20f));
						((Rect)(ref _window)).x = Mathf.Max(0f, (float)Screen.width - num - 16f);
						((Rect)(ref _window)).y = Mathf.Max(0f, ((float)Screen.height - num2) * 0.5f);
					}
					catch
					{
					}
				}
				Rect window = _window;
				((Rect)(ref window)).width = Mathf.Max(1f, Mathf.Min(((Rect)(ref _window)).width, (float)Screen.width - 20f));
				((Rect)(ref window)).height = Mathf.Max(1f, Mathf.Min(((Rect)(ref _window)).height, (float)Screen.height - 20f));
				((Rect)(ref window)).x = Mathf.Clamp(((Rect)(ref _window)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref window)).width));
				((Rect)(ref window)).y = Mathf.Clamp(((Rect)(ref _window)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref window)).height));
				_drawW = ((Rect)(ref window)).width;
				_drawH = ((Rect)(ref window)).height;
				Vector2 val = (Vector2)((Event.current != null) ? Event.current.mousePosition : new Vector2(-1000f, -1000f));
				_mouseInWindow = ((Rect)(ref window)).Contains(val);
				Rect val2 = GUI.Window(1213482033, window, new WindowFunction(DrawWindow), "", _ui.Window);
				((Rect)(ref _window)).x = Mathf.Clamp(((Rect)(ref val2)).x, 0f, Mathf.Max(0f, (float)Screen.width - 40f));
				((Rect)(ref _window)).y = Mathf.Clamp(((Rect)(ref val2)).y, 0f, Mathf.Max(0f, (float)Screen.height - 40f));
			}
			catch (Exception ex)
			{
				Plugin.LogError("OnGUI failed: " + ex);
			}
		}

		private void EnsureUi()
		{
			int num = 16;
			try
			{
				if (Plugin.CfgFontSize != null)
				{
					num = Mathf.Clamp(Plugin.CfgFontSize.Value, 10, 28);
				}
			}
			catch
			{
			}
			if (_ui == null || num != _uiFontSize)
			{
				try
				{
					_ui?.Dispose();
				}
				catch
				{
				}
				_uiFontSize = num;
				_ui = new UiTheme(num);
			}
		}

		private void DrawWindow(int id)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Invalid comparison between Unknown and I4
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: 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_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Invalid comparison between Unknown and I4
			float drawW = _drawW;
			float drawH = _drawH;
			DrawTopBar(drawW);
			GUILayout.BeginArea(new Rect(0f, 58f, drawW, Mathf.Max(1f, drawH - 58f)));
			float num = EaseOutCubic(Mathf.Clamp01(_pageAnim));
			Color color = GUI.color;
			GUI.color = new Color(1f, 1f, 1f, Mathf.Clamp01(num * 1.2f));
			_scroll = GUILayout.BeginScrollView(_scroll, false, false, GUIStyle.none, _ui.Scrollbar, GUIStyle.none, Array.Empty<GUILayoutOption>());
			GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
			GUILayout.Space(18f);
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) });
			GUILayout.Space(14f + (1f - num) * 14f);
			try
			{
				switch (_page)
				{
				case Page.Home:
					DrawHome();
					break;
				case Page.Host:
					DrawHost();
					break;
				case Page.Join:
					DrawJoin();
					break;
				case Page.Config:
					DrawConfig();
					break;
				}
			}
			catch (Exception ex)
			{
				Plugin.LogError("Page draw failed: " + ex);
			}
			GUILayout.Space(18f);
			GUILayout.EndVertical();
			GUILayout.Space(18f);
			GUILayout.EndHorizontal();
			Event current = Event.current;
			bool num2 = _mouseInWindow && current != null && (int)current.type == 6;
			Vector2 val = default(Vector2);
			if (num2)
			{
				val = current.delta;
				current.delta = new Vector2(val.x, val.y * ScrollMultiplier());
			}
			GUILayout.EndScrollView();
			if (num2)
			{
				current.delta = val;
			}
			GUI.color = color;
			GUILayout.EndArea();
			if (Event.current != null && (int)Event.current.type == 1)
			{
				_hotKey = null;
			}
			DrawToast(drawW, drawH);
			GUI.DragWindow(new Rect(0f, 0f, drawW, 58f));
		}

		private void DrawTopBar(float W)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: 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_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_030f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_034c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0364: Unknown result type (might be due to invalid IL or missing references)
			//IL_038d: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_041d: Unknown result type (might be due to invalid IL or missing references)
			//IL_042d: Unknown result type (might be due to invalid IL or missing references)
			float num = 18f;
			if (_page != Page.Home)
			{
				Rect r = default(Rect);
				((Rect)(ref r))..ctor(num, 12f, 34f, 34f);
				if (IconButton(r, "top.back", IconKind.ChevronLeft))
				{
					Go(Page.Home);
				}
				num += 46f;
			}
			else
			{
				Rect r2 = default(Rect);
				((Rect)(ref r2))..ctor(num, 14f, 30f, 30f);
				_ui.Fill(r2, 15f, _ui.Primary);
				_ui.Ring(new Rect(((Rect)(ref r2)).x + 8f, ((Rect)(ref r2)).y + 8f, 14f, 14f), 7f, new Color(1f, 1f, 1f, 0.92f));
				num += 42f;
			}
			string s = PageTitle(_page);
			string text = PageCaption(_page);
			float num2 = Mathf.Max(80f, W - num - 214f);
			if (string.IsNullOrEmpty(text))
			{
				_ui.DrawText(new Rect(num, 16f, num2, 26f), s, _ui.Title);
			}
			else
			{
				_ui.DrawText(new Rect(num, 5f, num2, 26f), s, _ui.Title);
				_ui.DrawText(new Rect(num, 31f, num2, 18f), text, _ui.Caption);
			}
			string s2 = ToggleKeyName();
			Vector2 val = _ui.TextSize(s2, _ui.Caption);
			string s3 = Loc.T("显示/隐藏", "show/hide");
			Vector2 val2 = _ui.TextSize(s3, _ui.Caption);
			float num3 = val.x + 18f;
			float num4 = num3 + 8f + val2.x + 20f;
			Rect r3 = default(Rect);
			((Rect)(ref r3))..ctor(W - 18f - 30f - 12f - num4, 15f, num4, 28f);
			_ui.Fill(r3, 14f, new Color(1f, 1f, 1f, 0.05f));
			_ui.Ring(r3, 14f, _ui.Border);
			Rect r4 = default(Rect);
			((Rect)(ref r4))..ctor(((Rect)(ref r3)).x + 8f, ((Rect)(ref r3)).y + 5f, num3, 18f);
			_ui.Fill(r4, 5f, new Color(1f, 1f, 1f, 0.1f));
			_ui.Ring(r4, 5f, _ui.BorderHi);
			float x = _ui.TextSize(s2, _ui.Caption).x;
			_ui.DrawText(new Rect(((Rect)(ref r4)).center.x - x * 0.5f, ((Rect)(ref r4)).y, x + 2f, ((Rect)(ref r4)).height), s2, _ui.Caption, _ui.Text);
			_ui.DrawText(new Rect(((Rect)(ref r4)).xMax + 6f, ((Rect)(ref r3)).y, val2.x + 6f, ((Rect)(ref r3)).height), s3, _ui.Caption);
			Rect r5 = default(Rect);
			((Rect)(ref r5))..ctor(W - 18f - 30f, 14f, 30f, 30f);
			if (IconButton(r5, "top.close", IconKind.Close))
			{
				HidePanel();
			}
			_ui.Fill(new Rect(16f, 57f, W - 32f, 1f), 0f, _ui.Divider);
		}

		private static string PageTitle(Page p)
		{
			return p switch
			{
				Page.Host => Loc.T("创建房间", "Create Room"), 
				Page.Join => Loc.T("加入房间", "Join Room"), 
				Page.Config => Loc.T("模组配置", "Mod Settings"), 
				_ => Loc.T("How to Fish · 直连", "How to Fish · Direct"), 
			};
		}

		private static string PageCaption(Page p)
		{
			return p switch
			{
				Page.Host => Loc.T("作为主机开一局", "Host a game"), 
				Page.Join => Loc.T("连接到好友的世界", "Join a friend's world"), 
				Page.Config => Loc.T("外观、行为与诊断", "Appearance, behavior & diagnostics"), 
				_ => Loc.T("IP 直连联机 · 无需 Steam", "IP direct connect · no Steam"), 
			};
		}

		private void DrawHome()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			DrawStatusCard();
			GUILayout.Space(16f);
			Rect r = Row(20f);
			_ui.DrawText(r, Loc.T("选择一项开始", "Pick one to start"), _ui.Muted);
			GUILayout.Space(8f);
			NavCard("home.host", IconKind.Plus, _ui.Primary, Loc.T("创建房间", "Create Room"), Loc.T("作为主机开一局,把本机 IP 发给朋友", "Host a game and send your IP to a friend"), enabled: true, Page.Host);
			GUILayout.Space(12f);
			NavCard("home.join", IconKind.ArrowRight, _ui.Amber, Loc.T("加入房间", "Join Room"), Loc.T("输入好友的 IP,进入他的世界", "Enter a friend's IP to join their world"), enabled: true, Page.Join);
			GUILayout.Space(12f);
			NavCard("home.config", IconKind.Sliders, _ui.Blue, Loc.T("模组配置", "Mod Settings"), Loc.T("字体、滚轮、快捷键与诊断信息", "Font, scroll, hotkey & diagnostics"), enabled: true, Page.Config);
			GUILayout.Space(12f);
			DrawQuickGuide();
			if (DirectNet.DirectSessionActive)
			{
				GUILayout.Space(18f);
				Rect r2 = Row(48f);
				if (PrimaryButton(r2, Loc.T("断开连接", "Disconnect"), IconKind.Power, _ui.Danger, _ui.DangerHi, _ui.OnDanger, enabled: true, "home.disconnect"))
				{
					StopWatchdog();
					DirectTransport.Disconnect();
				}
			}
			GUILayout.Space(18f);
			DrawFooter();
		}

		private void DrawStatusCard()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			Rect r = Row(68f);
			_ui.Fill(r, 12f, _ui.Card);
			_ui.Ring(r, 12f, _ui.Border);
			Color c = StatusColor();
			float a = ((DirectNet.DirectSessionActive && !DirectTransport.SessionReady) ? (0.4f + 0.6f * (0.5f + 0.5f * Mathf.Sin(_pulse * 5f))) : 1f);
			Rect r2 = default(Rect);
			((Rect)(ref r2))..ctor(((Rect)(ref r)).x + 18f, ((Rect)(ref r)).center.y - 7f, 14f, 14f);
			_ui.Fill(r2, 7f, UiTheme.WithA(c, a));
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 44f, ((Rect)(ref r)).y + 12f, ((Rect)(ref r)).width - 60f, 24f), StatusTitle(), _ui.CardTitle);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 44f, ((Rect)(ref r)).y + 37f, ((Rect)(ref r)).width - 60f, 20f), StatusDetail(), _ui.Muted);
		}

		private string StatusTitle()
		{
			if (!Plugin.Ready)
			{
				return Loc.T("初始化失败", "Initialization failed");
			}
			if (!string.IsNullOrEmpty(DirectNet.LastRejectReason))
			{
				return Loc.T("连接被拒绝", "Connection rejected");
			}
			if (DirectTransport.SessionReady)
			{
				return Loc.T("已连接", "Connected");
			}
			if (DirectNet.DirectSessionActive)
			{
				return Loc.T("连接中…", "Connecting…");
			}
			return Loc.T("未连接", "Not connected");
		}

		private string StatusDetail()
		{
			if (!Plugin.Ready)
			{
				return Loc.T("补丁或序列化器未生效(本模组适配 1.1.3)", "Patches or serializers failed (this mod targets 1.1.3)");
			}
			if (!string.IsNullOrEmpty(DirectNet.LastRejectReason))
			{
				return DirectNet.LastRejectReason;
			}
			if (DirectTransport.SessionReady)
			{
				return Loc.T("已进入房间,按 " + ToggleKeyName() + " 可隐藏面板", "In the room. Press " + ToggleKeyName() + " to hide the panel");
			}
			if (DirectNet.DirectSessionActive)
			{
				return Loc.T("正在与主机建立连接…", "Connecting to the host…");
			}
			if (!InMenu)
			{
				return Loc.T("游戏中(回到主菜单才能创建/加入)", "In game (return to the main menu to host/join)");
			}
			return Loc.T("在主菜单选择「创建房间」或「加入房间」", "Choose Create Room or Join Room in the main menu");
		}

		private Color StatusColor()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Ready)
			{
				return _ui.Danger;
			}
			if (!string.IsNullOrEmpty(DirectNet.LastRejectReason))
			{
				return _ui.Danger;
			}
			if (DirectTransport.SessionReady)
			{
				return _ui.Primary;
			}
			if (DirectNet.DirectSessionActive)
			{
				return _ui.Amber;
			}
			return _ui.TextDim;
		}

		private void DrawFooter()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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)
			Rect r = Row(22f);
			string s = Loc.T("HowToFishDirect v1.3.0\u3000·\u3000适配游戏 1.1.3", "HowToFishDirect v1.3.0 · for game 1.1.3");
			_ui.DrawText(r, s, _ui.Caption);
		}

		private void DrawQuickGuide()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: 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_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			Rect r = Row(104f);
			_ui.Fill(r, 12f, new Color(1f, 1f, 1f, 0.025f));
			_ui.Ring(r, 12f, _ui.Divider);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 16f, ((Rect)(ref r)).y + 12f, ((Rect)(ref r)).width - 32f, 20f), Loc.T("快速上手", "Quick start"), _ui.Section);
			string[] array = new string[3]
			{
				Loc.T("主机:进入「创建房间」,点一下复制本机 IP", "Host: open Create Room and click to copy your IP"),
				Loc.T("把 IP 与端口发给朋友(同一虚拟局域网最省事)", "Send the IP and port to your friend (a virtual LAN is easiest)"),
				Loc.T("朋友:进入「加入房间」,填入 IP 后点「加入房间」", "Friend: open Join Room, enter the IP and click Join Room")
			};
			Rect r2 = default(Rect);
			for (int i = 0; i < array.Length; i++)
			{
				float num = ((Rect)(ref r)).y + 38f + (float)i * 24f;
				((Rect)(ref r2))..ctor(((Rect)(ref r)).x + 18f, num + 2f, 18f, 18f);
				_ui.Fill(r2, 9f, UiTheme.WithA(_ui.Primary, 0.18f));
				string s = (i + 1).ToString();
				float x = _ui.TextSize(s, _ui.Caption).x;
				_ui.DrawText(new Rect(((Rect)(ref r2)).center.x - x * 0.5f, ((Rect)(ref r2)).y, x + 2f, ((Rect)(ref r2)).height), s, _ui.Caption, _ui.Primary);
				_ui.DrawText(new Rect(((Rect)(ref r)).x + 46f, num, ((Rect)(ref r)).width - 62f, 22f), array[i], _ui.Muted);
			}
		}

		private void DrawHost()
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_024d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			bool inMenu = InMenu;
			bool flag = CanAct();
			if (!flag)
			{
				Banner(inMenu ? Loc.T("补丁未生效,无法创建房间", "Patches not applied — cannot host") : Loc.T("仅能在主菜单创建房间,请先回到主菜单", "You can only host from the main menu"), warn: true);
			}
			Section(Loc.T("存档", "Save"));
			if (inMenu)
			{
				EnsureSaveList(force: false);
				DrawSaveSection();
			}
			else
			{
				Rect r = Row(26f);
				_ui.DrawText(r, Loc.T("当前:", "Current: ") + SaveName() + Loc.T("\u3000(游戏中不可更改)", " (cannot change in game)"), _ui.Muted);
			}
			Section(Loc.T("房间设置", "Room"));
			FieldRow(Loc.T("UDP 端口", "UDP port"), ref _port, "7777", 6);
			_maxPlayers = StepperRow(Loc.T("总人数(含主机)", "Max players (incl. host)"), _maxPlayers, 1, 8, "host.max");
			GUILayout.Space(12f);
			Rect r2 = Row(52f);
			if (PrimaryButton(r2, Loc.T("创建房间", "Create Room"), IconKind.Plus, _ui.Primary, _ui.PrimaryHi, _ui.OnPrimary, flag, "host.create"))
			{
				StartHost();
			}
			Section(Loc.T("本机 IP(点击复制)", "Local IP (click to copy)"));
			Rect val = Row(24f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 88f, 24f), Loc.T("把其中一个发给朋友", "Send one to your friend"), _ui.Caption);
			Rect r3 = default(Rect);
			((Rect)(ref r3))..ctor(((Rect)(ref val)).xMax - 84f, ((Rect)(ref val)).y, 84f, 24f);
			if (GhostButton(r3, Loc.T("刷新", "Refresh"), "host.refresh", enabled: true))
			{
				RefreshLocalIps();
			}
			GUILayout.Space(4f);
			DrawIpList();
			GUILayout.Space(14f);
			Rect r4 = Row(46f);
			_ui.DrawText(r4, Loc.T("提示:若双方不在同一局域网,请用内网穿透工具(如 Radmin / ZeroTier)后再填入其 IP。", "Tip: if you are not on the same LAN, use a virtual-LAN tool (Radmin / ZeroTier) and enter that IP."), _ui.Wrap);
		}

		private void DrawIpList()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: 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_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			if (_ipValues.Count == 0)
			{
				Rect r = Row(24f);
				_ui.DrawText(r, Loc.T("(未检测到可用 IP)", "(no usable IP found)"), _ui.Muted);
				return;
			}
			for (int i = 0; i < _ipValues.Count; i++)
			{
				string text = _ipValues[i];
				Rect r2 = Row(36f);
				bool hovered = ((Rect)(ref r2)).Contains(Event.current.mousePosition);
				float num = Hover("ip." + text, hovered, 10f);
				_ui.Fill(r2, 8f, new Color(1f, 1f, 1f, 0.04f * num));
				_ui.Icon(new Rect(((Rect)(ref r2)).x + 10f, ((Rect)(ref r2)).center.y - 8f, 16f, 16f), IconKind.Copy, Color.Lerp(_ui.TextDim, _ui.Primary, num));
				_ui.DrawText(new Rect(((Rect)(ref r2)).x + 34f, ((Rect)(ref r2)).y, ((Rect)(ref r2)).width - 120f, ((Rect)(ref r2)).height), _ipLabels[i], _ui.Body);
				_ui.DrawText(new Rect(((Rect)(ref r2)).xMax - 96f, ((Rect)(ref r2)).y, 86f, ((Rect)(ref r2)).height), Loc.T("点击复制", "click to copy"), _ui.Caption, UiTheme.WithA(_ui.TextMuted, 0.4f + 0.6f * num));
				if (Click(r2, enabled: true, "ip." + text))
				{
					CopyToClipboard(text);
				}
			}
		}

		private void DrawJoin()
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			bool flag = CanAct();
			if (!flag)
			{
				Banner(InMenu ? Loc.T("补丁未生效,无法加入房间", "Patches not applied — cannot join") : Loc.T("仅能在主菜单加入房间,请先回到主菜单", "You can only join from the main menu"), warn: true);
			}
			Section(Loc.T("连接信息", "Connection"));
			FieldRow(Loc.T("主机 IP", "Host IP"), ref _joinIp, Loc.T("例如 192.168.0.10 或域名", "e.g. 192.168.0.10 or a domain"), 128);
			FieldRow(Loc.T("UDP 端口", "UDP port"), ref _joinPort, "7777", 6);
			GUILayout.Space(12f);
			Rect r = Row(52f);
			if (PrimaryButton(r, Loc.T("加入房间", "Join Room"), IconKind.ArrowRight, _ui.Amber, _ui.AmberHi, _ui.OnAmber, flag, "join.connect"))
			{
				StartJoin();
			}
			Section(Loc.T("最近房间", "Recent rooms"));
			DrawHistory();
		}

		private void DrawHistory()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(24f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 76f, 24f), (_history.Count == 0) ? Loc.T("暂无记录", "No records") : Loc.T("共 " + _history.Count + " 条,点击填入", _history.Count + " entries, click to fill"), _ui.Muted);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((Rect)(ref val)).xMax - 68f, ((Rect)(ref val)).y, 68f, 24f);
			if (GhostButton(r, Loc.T("清空", "Clear"), "join.clear", _history.Count > 0))
			{
				_history.Clear();
				SaveHistory();
			}
			GUILayout.Space(4f);
			if (_history.Count == 0)
			{
				Rect r2 = Row(24f);
				_ui.DrawText(r2, Loc.T("成功加入一次后会自动记录。", "Saved automatically after you join a room once."), _ui.Caption);
				return;
			}
			for (int i = 0; i < _history.Count; i++)
			{
				string text = _history[i];
				Rect r3 = Row(36f);
				bool hovered = ((Rect)(ref r3)).Contains(Event.current.mousePosition);
				float num = Hover("hist." + text, hovered, 10f);
				_ui.Fill(r3, 8f, new Color(1f, 1f, 1f, 0.04f * num));
				_ui.Icon(new Rect(((Rect)(ref r3)).x + 10f, ((Rect)(ref r3)).center.y - 8f, 16f, 16f), IconKind.ArrowRight, Color.Lerp(_ui.TextDim, _ui.Amber, num));
				_ui.DrawText(new Rect(((Rect)(ref r3)).x + 34f, ((Rect)(ref r3)).y, ((Rect)(ref r3)).width - 110f, ((Rect)(ref r3)).height), text, _ui.Body);
				_ui.DrawText(new Rect(((Rect)(ref r3)).xMax - 80f, ((Rect)(ref r3)).y, 70f, ((Rect)(ref r3)).height), Loc.T("填入", "fill"), _ui.Caption, UiTheme.WithA(_ui.TextMuted, 0.4f + 0.6f * num));
				if (Click(r3, enabled: true, "hist." + text))
				{
					ApplyHistory(text);
				}
			}
		}

		private void DrawConfig()
		{
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			Section(Loc.T("外观", "Appearance"));
			int num = ((Plugin.CfgFontSize != null) ? Plugin.CfgFontSize.Value : 16);
			int num2 = StepperRow(Loc.T("字号", "Font size"), num, 10, 28, "cfg.font");
			if (num2 != num && Plugin.CfgFontSize != null)
			{
				Plugin.CfgFontSize.Value = num2;
			}
			int num3 = ((Plugin.CfgScrollSpeed != null) ? Plugin.CfgScrollSpeed.Value : 3);
			int num4 = StepperRow(Loc.T("滚轮速度", "Scroll speed"), num3, 1, 8, "cfg.scroll");
			if (num4 != num3 && Plugin.CfgScrollSpeed != null)
			{
				Plugin.CfgScrollSpeed.Value = num4;
			}
			Section(Loc.T("行为", "Behavior"));
			bool flag = Plugin.CfgShowOverlay != null && Plugin.CfgShowOverlay.Value;
			bool flag2 = ToggleRow("cfg.show", Loc.T("显示面板", "Show panel"), Loc.T("关闭后可用快捷键再次打开", "Reopen with the hotkey after closing"), flag, enabled: true);
			if (flag2 != flag)
			{
				if (Plugin.CfgShowOverlay != null)
				{
					Plugin.CfgShowOverlay.Value = flag2;
				}
				_visible = flag2;
				if (!flag2)
				{
					_awaitingKey = false;
				}
			}
			bool flag3 = Plugin.CfgVerbose != null && Plugin.CfgVerbose.Value;
			bool flag4 = ToggleRow("cfg.verbose", Loc.T("详细日志", "Verbose log"), Loc.T("在 BepInEx 日志中记录更多调试信息", "Write extra debug info to the BepInEx log"), flag3, enabled: true);
			if (flag4 != flag3 && Plugin.CfgVerbose != null)
			{
				Plugin.CfgVerbose.Value = flag4;
			}
			KeyCaptureRow();
			Section(Loc.T("关于", "About"));
			AboutRow(Loc.T("模组版本", "Mod version"), "v1.3.0");
			AboutRow(Loc.T("适配游戏", "Game version"), "1.1.3" + Loc.T("(当前 ", " (current ") + Application.version + Loc.T(")", ")"));
			AboutRow(Loc.T("补丁状态", "Patch status"), Plugin.PatchesCriticalOk ? Loc.T("正常", "OK") : Loc.T("异常", "Failed"), Plugin.PatchesCriticalOk ? _ui.Primary : _ui.Danger);
			AboutRow(Loc.T("本机 ID", "Install ID"), ShortId(Plugin.InstallId));
			Section(Loc.T("诊断", "Diagnostics"));
			DrawDiagnostics();
			GUILayout.Space(14f);
			Rect r = Row(44f);
			if (GhostButton(r, Loc.T("恢复默认设置", "Reset to defaults"), "cfg.reset", enabled: true))
			{
				ResetConfig();
			}
		}

		private void KeyCaptureRow()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(48f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 200f, ((Rect)(ref val)).height), Loc.T("切换按键", "Toggle key"), _ui.Body);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((Rect)(ref val)).xMax - 190f, ((Rect)(ref val)).center.y - 18f, 190f, 36f);
			string label = (_awaitingKey ? Loc.T("请按下任意键…(Esc 取消)", "Press any key… (Esc to cancel)") : (ToggleKeyName() + Loc.T("\u3000点击修改", " click to change")));
			if (GhostButton(r, label, "cfg.key", enabled: true))
			{
				_awaitingKey = true;
			}
		}

		private void AboutRow(string label, string value, Color? valueColor = null)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(30f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, 150f, ((Rect)(ref val)).height), label, _ui.Muted);
			_ui.DrawText(new Rect(((Rect)(ref val)).x + 150f, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 150f, ((Rect)(ref val)).height), value, _ui.Body, (Color)(((??)valueColor) ?? _ui.Text));
		}

		private void DrawDiagnostics()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			//IL_0048: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			string text = DirectTransport.BuildStatusText();
			float num = Mathf.Max(60f, _drawW - 36f - 11f - 24f);
			float num2 = _ui.Wrap.CalcHeight(new GUIContent(text), num);
			Rect r = Row(num2 + 12f);
			_ui.Fill(r, 8f, new Color(1f, 1f, 1f, 0.03f));
			_ui.Ring(r, 8f, _ui.Divider);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 12f, ((Rect)(ref r)).y + 6f, ((Rect)(ref r)).width - 24f, num2), text, _ui.Wrap);
		}

		private void ResetConfig()
		{
			try
			{
				if (Plugin.CfgFontSize != null)
				{
					Plugin.CfgFontSize.Value = 16;
				}
				if (Plugin.CfgScrollSpeed != null)
				{
					Plugin.CfgScrollSpeed.Value = 3;
				}
				if (Plugin.CfgToggleKey != null)
				{
					Plugin.CfgToggleKey.Value = "F7";
				}
				if (Plugin.CfgShowOverlay != null)
				{
					Plugin.CfgShowOverlay.Value = true;
				}
				if (Plugin.CfgVerbose != null)
				{
					Plugin.CfgVerbose.Value = true;
				}
				ShowToast(Loc.T("已恢复默认设置", "Defaults restored"), 1);
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("ResetConfig failed: " + ex.Message);
			}
		}

		private static string ShortId(string id)
		{
			if (string.IsNullOrEmpty(id))
			{
				return Loc.T("(未知)", "(unknown)");
			}
			if (id.Length > 12)
			{
				return id.Substring(0, 12) + "…";
			}
			return id;
		}

		private static Rect Row(float h)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			return GUILayoutUtility.GetRect(GUIContent.none, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[2]
			{
				GUILayout.ExpandWidth(true),
				GUILayout.Height(h)
			});
		}

		private bool Click(Rect r, bool enabled, string key)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			if (!enabled)
			{
				return false;
			}
			Event current = Event.current;
			if (current == null)
			{
				return false;
			}
			if ((int)current.type == 0)
			{
				if (current.button == 0 && ((Rect)(ref r)).Contains(current.mousePosition))
				{
					_hotKey = key;
					GUIUtility.keyboardControl = 0;
					current.Use();
				}
				return false;
			}
			if ((int)current.type == 1 && current.button == 0 && _hotKey == key)
			{
				_hotKey = null;
				if (((Rect)(ref r)).Contains(current.mousePosition))
				{
					current.Use();
					return true;
				}
			}
			return false;
		}

		private float Hover(string key, bool hovered, float speed)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if (!_hover.TryGetValue(key, out var value))
			{
				value = 0f;
			}
			Event current = Event.current;
			if (current != null && (int)current.type == 7)
			{
				value = Mathf.MoveTowards(value, hovered ? 1f : 0f, Time.unscaledDeltaTime * speed);
				_hover[key] = value;
			}
			return value;
		}

		private bool IconButton(Rect r, string key, IconKind icon, bool enabled = true, float iconSize = 18f)
		{
			//IL_000b: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			bool hovered = enabled && ((Rect)(ref r)).Contains(Event.current.mousePosition);
			float num = Hover(key, hovered, 12f);
			_ui.Fill(r, 8f, new Color(1f, 1f, 1f, 0.07f * num));
			_ui.Icon(UiTheme.Center(r, iconSize, iconSize), icon, enabled ? Color.Lerp(_ui.TextMuted, _ui.Text, num) : UiTheme.WithA(_ui.TextDim, 0.5f));
			return Click(r, enabled, key);
		}

		private bool GhostButton(Rect r, string label, string key, bool enabled)
		{
			//IL_000b: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			bool hovered = enabled && ((Rect)(ref r)).Contains(Event.current.mousePosition);
			float num = Hover(key, hovered, 10f);
			_ui.Fill(r, 9f, new Color(1f, 1f, 1f, 0.05f + 0.05f * num));
			_ui.Ring(r, 9f, Color.Lerp(_ui.Border, _ui.BorderHi, num));
			_ui.DrawText(r, label, _ui.ButtonText, enabled ? Color.Lerp(_ui.TextMuted, _ui.Text, num) : UiTheme.WithA(_ui.TextMuted, 0.5f));
			return Click(r, enabled, key);
		}

		private bool PrimaryButton(Rect r, string label, IconKind icon, Color fill, Color fillHi, Color textColor, bool enabled, string key)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			bool hovered = enabled && ((Rect)(ref r)).Contains(Event.current.mousePosition);
			float num = Hover(key, hovered, 10f);
			Color color = (enabled ? Color.Lerp(fill, fillHi, num) : UiTheme.WithA(fill, 0.32f));
			_ui.Fill(r, 10f, color);
			if (enabled && num > 0.01f)
			{
				_ui.Ring(r, 10f, UiTheme.WithA(Color.white, 0.12f * num));
			}
			Color val = (enabled ? textColor : UiTheme.WithA(textColor, 0.5f));
			float x = _ui.TextSize(label, _ui.ButtonText).x;
			float num2 = x + 28f;
			float num3 = ((Rect)(ref r)).center.x - num2 * 0.5f;
			_ui.Icon(new Rect(num3, ((Rect)(ref r)).center.y - 10f, 20f, 20f), icon, val);
			_ui.DrawText(new Rect(num3 + 28f, ((Rect)(ref r)).y, x + 6f, ((Rect)(ref r)).height), label, _ui.ButtonText, val);
			return Click(r, enabled, key);
		}

		private bool NavCard(string key, IconKind icon, Color accent, string title, string desc, bool enabled, Page target)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			Rect r = Row(92f);
			bool hovered = enabled && ((Rect)(ref r)).Contains(Event.current.mousePosition);
			float num = Hover(key, hovered, 9f);
			_ui.Fill(r, 12f, Color.Lerp(_ui.Card, _ui.CardHover, num));
			_ui.Ring(r, 12f, Color.Lerp(_ui.Border, UiTheme.WithA(accent, 0.55f), num));
			_ui.Fill(new Rect(((Rect)(ref r)).x + 1f, ((Rect)(ref r)).y + 16f, 3f, ((Rect)(ref r)).height - 32f), 1.5f, UiTheme.WithA(accent, 0.9f));
			Rect r2 = default(Rect);
			((Rect)(ref r2))..ctor(((Rect)(ref r)).x + 20f, ((Rect)(ref r)).center.y - 19f, 38f, 38f);
			_ui.Fill(r2, 11f, UiTheme.WithA(accent, 0.14f));
			_ui.Icon(new Rect(((Rect)(ref r2)).x + 9f, ((Rect)(ref r2)).y + 9f, 20f, 20f), icon, accent);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 74f, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width - 74f - 46f, 24f), title, _ui.CardTitle);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 74f, ((Rect)(ref r)).y + 44f, ((Rect)(ref r)).width - 74f - 46f, 36f), desc, _ui.CardSub);
			_ui.Icon(new Rect(((Rect)(ref r)).xMax - 36f + num * 5f, ((Rect)(ref r)).center.y - 9f, 18f, 18f), IconKind.ChevronRight, Color.Lerp(_ui.TextDim, accent, num));
			if (!enabled)
			{
				_ui.Fill(r, 12f, new Color(0f, 0f, 0f, 0.3f));
			}
			if (Click(r, enabled, key))
			{
				Go(target);
				return true;
			}
			return false;
		}

		private int StepperRow(string label, int value, int min, int max, string key)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(46f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 150f, ((Rect)(ref val)).height), label, _ui.Body);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((Rect)(ref val)).xMax - 34f, ((Rect)(ref val)).center.y - 17f, 34f, 34f);
			Rect r2 = default(Rect);
			((Rect)(ref r2))..ctor(((Rect)(ref r)).x - 52f, ((Rect)(ref val)).y, 52f, ((Rect)(ref val)).height);
			Rect r3 = default(Rect);
			((Rect)(ref r3))..ctor(((Rect)(ref r2)).x - 34f, ((Rect)(ref val)).center.y - 17f, 34f, 34f);
			int num = value;
			if (IconButton(r3, key + ".m", IconKind.Minus, value > min))
			{
				num = value - 1;
			}
			if (IconButton(r, key + ".p", IconKind.Plus, value < max))
			{
				num = value + 1;
			}
			_ui.DrawText(r2, num.ToString(), _ui.ButtonText, _ui.Text);
			return Mathf.Clamp(num, min, max);
		}

		private bool ToggleRow(string key, string label, string desc, bool value, bool enabled)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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)
			Rect r = Row(58f);
			_ui.DrawText(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 7f, ((Rect)(ref r)).width - 90f, 22f), label, _ui.Body);
			_ui.DrawText(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 30f, ((Rect)(ref r)).width - 90f, 18f), desc, _ui.Caption);
			Rect r2 = default(Rect);
			((Rect)(ref r2))..ctor(((Rect)(ref r)).xMax - 56f, ((Rect)(ref r)).center.y - 14f, 48f, 28f);
			bool hovered = enabled && ((Rect)(ref r)).Contains(Event.current.mousePosition);
			Hover(key, hovered, 10f);
			float num = Hover(key + ".v", value, 14f);
			Color val = Color.Lerp(new Color(1f, 1f, 1f, 0.13f), _ui.Primary, num);
			if (!enabled)
			{
				val = UiTheme.WithA(val, 0.4f);
			}
			_ui.Fill(r2, 14f, val);
			float num2 = Mathf.Lerp(((Rect)(ref r2)).x + 4f, ((Rect)(ref r2)).xMax - 24f, num);
			_ui.Fill(new Rect(num2, ((Rect)(ref r2)).y + 4f, 20f, 20f), 10f, Color.Lerp(_ui.TextMuted, Color.white, num));
			if (Click(r, enabled, key))
			{
				return !value;
			}
			return value;
		}

		private void Section(string text)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.Space(10f);
			Rect val = Row(22f);
			_ui.Fill(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 5f, 3f, 12f), 1.5f, _ui.Primary);
			_ui.DrawText(new Rect(((Rect)(ref val)).x + 11f, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 11f, 22f), text, _ui.Section);
			GUILayout.Space(4f);
		}

		private void Banner(string text, bool warn)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_0014: 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_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			Rect r = Row(42f);
			Color val = (warn ? _ui.Amber : _ui.Blue);
			_ui.Fill(r, 9f, UiTheme.WithA(val, 0.1f));
			_ui.Ring(r, 9f, UiTheme.WithA(val, 0.32f));
			_ui.Icon(new Rect(((Rect)(ref r)).x + 12f, ((Rect)(ref r)).center.y - 9f, 18f, 18f), warn ? IconKind.Warn : IconKind.Info, val);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 38f, ((Rect)(ref r)).y, ((Rect)(ref r)).width - 50f, ((Rect)(ref r)).height), text, _ui.Muted, _ui.Text);
			GUILayout.Space(10f);
		}

		private void FieldRow(string label, ref string value, string placeholder, int maxLen)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(42f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, 118f, ((Rect)(ref val)).height), label, _ui.Muted);
			Rect val2 = default(Rect);
			((Rect)(ref val2))..ctor(((Rect)(ref val)).x + 118f, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 118f, ((Rect)(ref val)).height);
			value = GUI.TextField(val2, value ?? "", maxLen, _ui.Field);
			if (string.IsNullOrEmpty(value))
			{
				_ui.DrawText(new Rect(((Rect)(ref val2)).x + 14f, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width - 24f, ((Rect)(ref val2)).height), placeholder, _ui.Placeholder);
			}
			GUILayout.Space(4f);
		}

		private void ShowToast(string text, int kind)
		{
			_toastText = text ?? "";
			_toastKind = kind;
			_toastAge = 0f;
		}

		private void DrawToast(float W, float H)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			if (!string.IsNullOrEmpty(_toastText))
			{
				if (_toastAge > 3.4f)
				{
					_toastText = "";
					return;
				}
				float num = Mathf.Min(1f, _toastAge / 0.16f) * Mathf.Min(1f, (3.4f - _toastAge) / 0.45f);
				num = Mathf.Clamp01(num);
				Color c = ((_toastKind == 2) ? _ui.Danger : ((_toastKind == 1) ? _ui.Primary : _ui.Blue));
				IconKind kind = ((_toastKind == 2) ? IconKind.Warn : ((_toastKind == 1) ? IconKind.Check : IconKind.Info));
				Vector2 val = _ui.TextSize(_toastText, _ui.Body);
				float num2 = Mathf.Min(W - 40f, val.x + 64f);
				float num3 = (W - num2) * 0.5f;
				float num4 = H - 26f - 44f + (1f - num) * 10f;
				Rect r = default(Rect);
				((Rect)(ref r))..ctor(num3, num4, num2, 44f);
				_ui.Fill(r, 10f, UiTheme.WithA(_ui.Card, 0.98f * num));
				_ui.Ring(r, 10f, UiTheme.WithA(c, 0.55f * num));
				_ui.Icon(new Rect(((Rect)(ref r)).x + 15f, ((Rect)(ref r)).center.y - 9f, 18f, 18f), kind, UiTheme.WithA(c, num));
				_ui.DrawText(new Rect(((Rect)(ref r)).x + 44f, ((Rect)(ref r)).y, ((Rect)(ref r)).width - 56f, ((Rect)(ref r)).height), _toastText, _ui.Body, UiTheme.WithA(_ui.Text, num));
			}
		}

		private void Go(Page p)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			_page = p;
			_pageAnim = 0f;
			_scroll = Vector2.zero;
			_awaitingKey = false;
		}

		private void HidePanel()
		{
			_visible = false;
			_awaitingKey = false;
			try
			{
				if (Plugin.CfgShowOverlay != null)
				{
					Plugin.CfgShowOverlay.Value = false;
				}
			}
			catch
			{
			}
		}

		private void StartHost()
		{
			if (!int.TryParse(_port, out var result) || result <= 0 || result >= 65536)
			{
				ShowToast(Loc.T("错误:UDP 端口不合法", "Error: invalid UDP port"), 2);
				Plugin.LogWarn("Invalid host port: " + _port);
				return;
			}
			if (Plugin.CfgPort != null)
			{
				Plugin.CfgPort.Value = result;
			}
			if (Plugin.CfgMaxPlayers != null)
			{
				Plugin.CfgMaxPlayers.Value = _maxPlayers;
			}
			if (ApplySaveSelection() && DirectTransport.Host(result, _maxPlayers))
			{
				StartWatchdog();
				Go(Page.Home);
			}
		}

		private void StartJoin()
		{
			if (!int.TryParse(_joinPort, out var result) || result <= 0 || result >= 65536)
			{
				ShowToast(Loc.T("错误:UDP 端口不合法", "Error: invalid UDP port"), 2);
				Plugin.LogWarn("Invalid join port: " + _joinPort);
				return;
			}
			string text = (_joinIp ?? "").Trim();
			if (Plugin.CfgJoinIp != null)
			{
				Plugin.CfgJoinIp.Value = text;
			}
			if (DirectTransport.Join(text, result))
			{
				_pendingHistoryIp = text;
				_pendingHistoryPort = result;
				StartWatchdog();
				Go(Page.Home);
			}
		}

		private static void CopyToClipboard(string value)
		{
			try
			{
				GUIUtility.systemCopyBuffer = value;
				DirectTransport.SetStatus(Loc.T("已复制 IP:" + value, "Copied IP: " + value));
			}
			catch (Exception ex)
			{
				DirectTransport.SetStatus(Loc.T("复制失败,请手动记录:" + value, "Copy failed, please note it down: " + value));
				Plugin.LogWarn("Clipboard copy failed: " + ex.Message);
			}
		}

		private void ApplyHistory(string entry)
		{
			try
			{
				int num = entry.LastIndexOf(':');
				if (num > 0)
				{
					_joinIp = entry.Substring(0, num);
					_joinPort = entry.Substring(num + 1);
					DirectTransport.SetStatus(Loc.T("已填入加入栏:" + entry, "Filled the join field: " + entry));
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("ApplyHistory failed: " + ex.Message);
			}
		}

		private static bool CanAct()
		{
			if (InMenu)
			{
				return Plugin.Ready;
			}
			return false;
		}

		private static float EaseOutCubic(float t)
		{
			t = Mathf.Clamp01(t);
			float num = 1f - t;
			return 1f - num * num * num;
		}

		private static float ScrollMultiplier()
		{
			try
			{
				if (Plugin.CfgScrollSpeed != null)
				{
					return Mathf.Clamp(Plugin.CfgScrollSpeed.Value, 1, 8);
				}
			}
			catch
			{
			}
			return 3f;
		}

		private static string ConfiguredToggleKey()
		{
			if (Plugin.CfgToggleKey == null)
			{
				return "F7";
			}
			return Plugin.CfgToggleKey.Value;
		}

		private unsafe static string ToggleKeyName()
		{
			if (!TryParseKey(ConfiguredToggleKey(), out var key))
			{
				return "F7";
			}
			return ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString();
		}

		private static bool TryParseKey(string value, out KeyCode key)
		{
			key = (KeyCode)288;
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			if (!Enum.TryParse<KeyCode>(value.Trim(), ignoreCase: true, out key))
			{
				return false;
			}
			try
			{
				return key && Enum.IsDefined(typeof(KeyCode), key);
			}
			catch
			{
				return (int)key != 0;
			}
		}

		private void HandleToggleKey()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (_awaitingKey)
			{
				return;
			}
			try
			{
				Event current = Event.current;
				if (current == null || (int)current.type != 4 || (GUIUtility.keyboardControl != 0 && current.character != 0))
				{
					return;
				}
				if (!TryParseKey(ConfiguredToggleKey(), out var key))
				{
					key = (KeyCode)288;
				}
				if (current.keyCode != key)
				{
					return;
				}
				_visible = !_visible;
				try
				{
					if (Plugin.CfgShowOverlay != null)
					{
						Plugin.CfgShowOverlay.Value = _visible;
					}
				}
				catch
				{
				}
				current.Use();
			}
			catch
			{
			}
		}

		private void HandleKeyCapture()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if (!_awaitingKey || !_visible)
			{
				return;
			}
			try
			{
				Event current = Event.current;
				if (current == null || (int)current.type != 4)
				{
					return;
				}
				if ((int)current.keyCode == 27)
				{
					_awaitingKey = false;
					current.Use();
				}
				else if ((int)current.keyCode != 0)
				{
					if (Plugin.CfgToggleKey != null)
					{
						Plugin.CfgToggleKey.Value = ((object)current.keyCode/*cast due to .constrained prefix*/).ToString();
					}
					_awaitingKey = false;
					ShowToast(Loc.T("切换按键已设为 ", "Toggle key set to ") + ((object)current.keyCode/*cast due to .constrained prefix*/).ToString(), 1);
					current.Use();
				}
			}
			catch
			{
			}
		}

		private string SaveName()
		{
			if (!string.IsNullOrEmpty(_selectedSaveName))
			{
				return _selectedSaveName;
			}
			return Loc.T("(未选择)", "(none)");
		}

		private void DrawSaveSection()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			Rect val = Row(28f);
			_ui.DrawText(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width - 76f, ((Rect)(ref val)).height), Loc.T("当前存档:", "Current save: ") + SaveName(), string.IsNullOrEmpty(_selectedSaveName) ? _ui.Muted : _ui.Success);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(((Rect)(ref val)).xMax - 72f, ((Rect)(ref val)).y, 72f, 28f);
			if (GhostButton(r, Loc.T("刷新", "Refresh"), "save.refresh", enabled: true))
			{
				EnsureSaveList(force: true);
			}
			GUILayout.Space(2f);
			List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
			try
			{
				if (SaveManager.ServerSaves != null)
				{
					foreach (ServerSaveObject serverSafe in SaveManager.ServerSaves)
					{
						if (serverSafe != null && !string.IsNullOrEmpty(serverSafe.Name))
						{
							list.Add(new KeyValuePair<string, string>(serverSafe.Name, Loc.T("岛 ", "Island ") + (serverSafe.SpawnedIsland + 1) + " · " + DifficultyName(serverSafe.Difficulty)));
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("DrawSaveSection list: " + ex.Message);
			}
			if (list.Count == 0)
			{
				Rect r2 = Row(24f);
				_ui.DrawText(r2, Loc.T("(暂无存档:请先用游戏原版的「新游戏」创建一个)", "(no saves yet: create one with the game's New Game first)"), _ui.Muted);
				return;
			}
			for (int i = 0; i < list.Count; i++)
			{
				string key = list[i].Key;
				if (SaveRow(key, list[i].Value, key == _selectedSaveName))
				{
					_selectedSaveName = key;
					try
					{
						SaveManager.SelectServer(key);
					}
					catch
					{
					}
				}
			}
		}

		private bool SaveRow(string name, string meta, bool selected)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			Rect r = Row(44f);
			bool hovered = ((Rect)(ref r)).Contains(Event.current.mousePosition);
			float num = Hover("save." + name, hovered, 10f);
			if (selected)
			{
				_ui.Fill(r, 8f, UiTheme.WithA(_ui.Primary, 0.1f));
			}
			else if (num > 0.01f)
			{
				_ui.Fill(r, 8f, new Color(1f, 1f, 1f, 0.04f * num));
			}
			if (selected)
			{
				_ui.Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 10f, 3f, ((Rect)(ref r)).height - 20f), 1.5f, _ui.Primary);
			}
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 14f, ((Rect)(ref r)).y + 3f, ((Rect)(ref r)).width - 120f, 20f), name, selected ? _ui.CardTitle : _ui.Body);
			_ui.DrawText(new Rect(((Rect)(ref r)).x + 14f, ((Rect)(ref r)).y + 23f, ((Rect)(ref r)).width - 120f, 16f), meta, _ui.Caption);
			bool flag = false;
			if (!selected)
			{
				Rect r2 = default(Rect);
				((Rect)(ref r2))..ctor(((Rect)(ref r)).xMax - 88f, ((Rect)(ref r)).center.y - 15f, 76f, 30f);
				if (GhostButton(r2, Loc.T("选择", "Select"), "savebtn." + name, enabled: true))
				{
					flag = true;
				}
			}
			else
			{
				_ui.DrawText(new Rect(((Rect)(ref r)).xMax - 88f, ((Rect)(ref r)).center.y - 12f, 76f, 24f), Loc.T("使用中", "In use"), _ui.Success, _ui.PrimaryHi);
			}
			if (!flag && Click(r, enabled: true, "save." + name))
			{
				flag = true;
			}
			return flag;
		}

		private void EnsureSaveList(bool force)
		{
			if (_saveListLoaded && !force)
			{
				return;
			}
			bool flag = false;
			try
			{
				flag = MainMenuManager.IsInMenu;
			}
			catch
			{
			}
			if (!flag)
			{
				return;
			}
			string text = null;
			try
			{
				text = ((SaveManager.CurServerSave != null) ? SaveManager.CurServerSave.Name : null);
			}
			catch
			{
			}
			try
			{
				SaveManager.LoadAllServers();
				_saveListLoaded = true;
			}
			catch (Exception ex)
			{
				Plugin.LogWarn("LoadAllServers failed: " + ex.Message);
				return;
			}
			if (!string.IsNullOrEmpty(text))
			{
				try
				{
					SaveManager.SelectServer(text);
				}
				catch
				{
				}
				if (SaveManager.CurServerSave != null)
				{
					_selectedSaveName = text;
					return;
				}
			}
			bool flag2 = false;
			string text2 = null;
			long num = long.MinValue;
			try
			{
				if (SaveManager.ServerSaves != null)
				{
					foreach (ServerSaveObject serverSafe in SaveManager.ServerSaves)
					{
						if (serverSafe != null && !string.IsNullOrEmpty(serverSafe.Name))
						{
							if (serverSafe.Name == "IP Host")
							{
								flag2 = true;
							}
							if (serverSafe.LastSave > num)
							{
								num = serverSafe.LastSave;
								text2 = serverSafe.Name;
							}
						}
					}
				}
			}
			catch
			{
			}
			if (flag2)
			{
				_selectedSaveName = "IP Host";
			}
			else if (text2 != null)
			{
				_selectedSaveName = text2;
			}
			else
			{
				_selectedSaveName = "";
			}
		}

		private bool ApplySaveSelection()
		{
			try
			{
				if (string.IsNullOrEmpty(_selectedSaveName))
				{
					ShowToast(Loc.T("错误:请先在「存档」里选择一个存档", "Error: select a save first"), 2);
					return false;
				}
				SaveManager.OnServerLoaded();
				SaveManager.SelectServer(_selectedSaveName);
				if (SaveManager.CurServerSave == null)
				{
					SaveManager.LoadAllServers();
					SaveManager.SelectServer(_selectedSaveName);
				}
				if (SaveManager.CurServerSave == null)
				{
					ShowToast(Loc.T("错误:找不到存档 ", "Error: save not found: ") + _selectedSaveName, 2);
					return false;
				}
				Plugin.LogInfo("Hosting save '" + _selectedSaveName + "' in direct mode.");
				return true;
			}
			catch (Exception ex)
			{
				Plugin.LogError("ApplySaveSelection failed: " + ex);
				ShowToast(Loc.T("错误:", "Error: ") + ex.Message, 2);
				return false;
			}
		}

		private static string DifficultyName(Difficulty d)
		{
			//IL_0000: 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_0015: Invalid comparison between Unknown and I4
			if ((int)d == 0)
			{
				return Loc.T("简单", "Easy");
			}
			if ((int)d == 2)
			{
				return Loc.T("困难", "Hard");
			}
			return Loc.T("默认", "Default");
		}

		private void AddHistory(string ip, int port)
		{
			try
			{
				if (!string.IsNullOrEmpty(ip))
				{
					string entry = ip + ":" + port;
					_history.RemoveAll((string e) => string.Equals(e, entry, StringComparison.OrdinalIgnoreCase));
					_history.Insert(0, entry);
					while (_history.Count > 8)
					{
						_history.RemoveAt(_history.Count - 1);
					}
					SaveHistory();
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("AddHistory failed: " + ex.Message);
			}
		}

		private void LoadHistory()
		{
			try
			{
				_history.Clear();
				string text = ((Plugin.CfgHistory != null) ? Plugin.CfgHistory.Value : "");
				if (string.IsNullOrEmpty(text))
				{
					return;
				}
				string[] array = text.Split(new char[1] { '|' });
				foreach (string text2 in array)
				{
					string t = text2.Trim();
					if (t.Length != 0 && !_history.Exists((string e) => string.Equals(e, t, StringComparison.OrdinalIgnoreCase)))
					{
						_history.Add(t);
						if (_history.Count >= 8)
						{
							break;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("LoadHistory failed: " + ex.Message);
			}
		}

		private void SaveHistory()
		{
			try
			{
				if (Plugin.CfgHistory != null)
				{
					Plugin.CfgHistory.Value = string.Join("|", _history.ToArray());
				}
			}
			catch (Exception ex)
			{
				Plugin.LogVerbose("SaveHistory failed: " + ex.Message);
			}
		}

		private void StartWatchdog()
		{
			StopWatchdog();
			_watchdog = ((MonoBehaviour)this).StartCoroutine(WatchdogRoutine());
		}

		private void StopWatchdog()
		{
			if (_watchdog != null)
			{
				((MonoBehaviour)this).StopCoroutine(_watchdog);
				_watchdog = null;
			}
		}

		private IEnumerator WatchdogRoutine()
		{
			float t = 0f;
			while (t < 40f)
			{
				if (!DirectNet.DirectSessionActive)
				{
					_pendingHistoryIp = null;
					_watchdog = null;
					yield break;
				}
				if (!string.IsNullOrEmpty(DirectNet.LastRejectReason))
				{
					_pendingHistoryIp = null;
					_watchdog = null;
					DirectTransport.AbortToMenu(DirectNet.LastRejectReason);
					y