Decompiled source of ServerConnect v1.0.14

ServerConnect.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using NetworkingUtils;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ServerConnect")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ServerConnect")]
[assembly: AssemblyTitle("ServerConnect")]
[assembly: AssemblyVersion("1.0.0.0")]
[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 ServerConnect
{
	internal static class ConfigLogDump
	{
		internal static void Schedule(BaseUnityPlugin plugin, ManualLogSource log)
		{
			((MonoBehaviour)plugin).StartCoroutine(Run(plugin, log));
		}

		private static IEnumerator Run(BaseUnityPlugin plugin, ManualLogSource log)
		{
			yield return null;
			yield return null;
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				foreach (ConfigDefinition key in plugin.Config.Keys)
				{
					string text = key.Key.ToLowerInvariant();
					if (text.Contains("pass") || text.Contains("token") || text.Contains("secret"))
					{
						continue;
					}
					ConfigEntryBase val = plugin.Config[key];
					if (!object.Equals(val.BoxedValue, val.DefaultValue))
					{
						if (stringBuilder.Length > 0)
						{
							stringBuilder.Append("; ");
						}
						stringBuilder.Append(key.Section).Append('.').Append(key.Key)
							.Append('=')
							.Append(val.BoxedValue);
						if (stringBuilder.Length > 1500)
						{
							stringBuilder.Append("; ...");
							break;
						}
					}
				}
				log.LogInfo((object)("[ServerConnect] config (changed from default): " + ((stringBuilder.Length == 0) ? "none" : stringBuilder.ToString())));
			}
			catch (Exception ex)
			{
				log.LogWarning((object)("[ServerConnect] config dump failed: " + ex.Message));
			}
		}
	}
	[BepInPlugin("zenox.serverconnect", "ServerConnect", "1.0.14")]
	public class ServerConnectPlugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(FejdStartup), "Awake")]
		private static class FejdStartup_Awake_Patch
		{
			private static void Postfix(FejdStartup __instance)
			{
				//IL_008a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0094: Expected O, but got Unknown
				if (!ServerButtonEnabled.Value)
				{
					return;
				}
				Button[] componentsInChildren = __instance.m_menuList.GetComponentsInChildren<Button>(true);
				if (componentsInChildren.Length != 0)
				{
					Button obj = componentsInChildren[0];
					Button val = Object.Instantiate<Button>(obj, ((Component)obj).transform.parent);
					((Object)val).name = "ServerConnectButton";
					TMP_Text componentInChildren = ((Component)val).GetComponentInChildren<TMP_Text>(true);
					if ((Object)(object)componentInChildren != (Object)null)
					{
						componentInChildren.text = ServerButtonLabel.Value;
					}
					((UnityEventBase)val.onClick).RemoveAllListeners();
					((UnityEvent)val.onClick).AddListener((UnityAction)delegate
					{
						ConnectToConfiguredServer(__instance);
					});
					if (ServerStatusCheckEnabled.Value && (Object)(object)componentInChildren != (Object)null)
					{
						((Component)val).gameObject.AddComponent<ServerStatusPoller>().Init(componentInChildren);
					}
					((Component)val).transform.SetSiblingIndex(0);
					RebuildVerticalNavigation(((Component)obj).transform.parent);
				}
			}

			private static void RebuildVerticalNavigation(Transform buttonParent)
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: 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)
				Button val = null;
				for (int i = 0; i < buttonParent.childCount; i++)
				{
					Button component = ((Component)buttonParent.GetChild(i)).GetComponent<Button>();
					if (!((Object)(object)component == (Object)null) && ((Component)component).gameObject.activeSelf)
					{
						Navigation navigation = ((Selectable)component).navigation;
						((Navigation)(ref navigation)).mode = (Mode)4;
						((Navigation)(ref navigation)).selectOnUp = (Selectable)(object)val;
						((Selectable)component).navigation = navigation;
						if ((Object)(object)val != (Object)null)
						{
							Navigation navigation2 = ((Selectable)val).navigation;
							((Navigation)(ref navigation2)).selectOnDown = (Selectable)(object)component;
							((Selectable)val).navigation = navigation2;
						}
						val = component;
					}
				}
			}
		}

		public const string PluginGUID = "zenox.serverconnect";

		public const string PluginName = "ServerConnect";

		public const string PluginVersion = "1.0.14";

		public static ConfigEntry<bool> ServerButtonEnabled;

		public static ConfigEntry<string> ServerButtonLabel;

		public static ConfigEntry<string> ServerButtonAddress;

		public static ConfigEntry<string> ServerButtonPassword;

		public static ConfigEntry<bool> ServerStatusCheckEnabled;

		public static ConfigEntry<int> ServerQueryPort;

		public static ConfigEntry<bool> ServerStatusDot;

		public static ConfigEntry<bool> PlayFabFallback;

		public static ConfigEntry<float> StatusCheckInterval;

		public static ConfigEntry<float> StatusCheckIntervalOnline;

		public static ConfigEntry<bool> StatusColorText;

		internal static ManualLogSource Log;

		private readonly Harmony harmony = new Harmony("zenox.serverconnect");

		private void Awake()
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Expected O, but got Unknown
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Expected O, but got Unknown
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Expected O, but got Unknown
			ConfigLogDump.Schedule((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger);
			ServerButtonEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ServerButton", "Enabled", true, "If true, adds a button to the main menu that connects straight to the configured server with the configured password, skipping the server browser.");
			ServerButtonLabel = ((BaseUnityPlugin)this).Config.Bind<string>("ServerButton", "Label", "My Server", "Text shown on the main menu button.");
			ServerButtonAddress = ((BaseUnityPlugin)this).Config.Bind<string>("ServerButton", "Address", "127.0.0.1:2456", "Server address (ip:port) the button connects to.");
			ServerButtonPassword = ((BaseUnityPlugin)this).Config.Bind<string>("ServerButton", "Password", "changeme", "Password submitted automatically when connecting via the button.");
			ServerStatusCheckEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("ServerButton", "StatusCheckEnabled", true, "If true, the button text stays grayed out until the server answers a status query (interval set by StatusCheckInterval / StatusCheckIntervalOnline) - the button itself always stays clickable. Set to false to turn the check off completely (no network traffic, text always normal).");
			ServerQueryPort = ((BaseUnityPlugin)this).Config.Bind<int>("ServerButton", "QueryPort", 0, new ConfigDescription("UDP port used for the status query. 0 = automatic (game port from Address + 1, the standard Steam query port Valheim dedicated servers use). Only set this if your server's query port differs.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 65535), Array.Empty<object>()));
			ServerStatusDot = ((BaseUnityPlugin)this).Config.Bind<bool>("ServerButton", "StatusDot", true, "If true, shows a small colored dot next to the button text: green = server online, red = offline, grey = no answer yet. Only used while StatusCheckEnabled is true.");
			PlayFabFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("ServerButton", "PlayFabFallback", true, "If true and the Steam query gets no answer (for example because the server runs with -crossplay), the status check asks PlayFab - the same lookup the game does when you join by IP - whether the server is registered and online. Only used while StatusCheckEnabled is true.");
			StatusCheckInterval = ((BaseUnityPlugin)this).Config.Bind<float>("ServerButton", "StatusCheckInterval", 10f, new ConfigDescription("Seconds between status checks while the server is shown as offline / not yet answered (1-60). While PlayFabFallback is on, values below 10 act as 10 (PlayFab lookups are rate limited).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			StatusCheckIntervalOnline = ((BaseUnityPlugin)this).Config.Bind<float>("ServerButton", "StatusCheckIntervalOnline", 60f, new ConfigDescription("Seconds between status checks once the server is shown as online (1-60).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
			StatusColorText = ((BaseUnityPlugin)this).Config.Bind<bool>("ServerButton", "StatusColorText", true, "If true, the button text is tinted green/red by the server status. Set to false to keep the normal menu text color - the StatusDot (if enabled) still shows the status. Only used while StatusCheckEnabled is true.");
			Log = ((BaseUnityPlugin)this).Logger;
			harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ServerConnect v1.0.14 loaded.");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private static void ConnectToConfiguredServer(FejdStartup instance)
		{
			//IL_0038: 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)
			typeof(FejdStartup).GetProperty("ServerPassword", BindingFlags.Static | BindingFlags.Public)?.SetValue(null, ServerButtonPassword.Value);
			ServerJoinData val = default(ServerJoinData);
			((ServerJoinData)(ref val))..ctor(new ServerJoinDataDedicated(ServerButtonAddress.Value));
			typeof(FejdStartup).GetMethod("ProceedJoinRequest", BindingFlags.Instance | BindingFlags.NonPublic)?.Invoke(instance, new object[1] { val });
		}
	}
	internal class ServerStatusPoller : MonoBehaviour
	{
		private const int QueryTimeoutMs = 2500;

		private const int QueryAttempts = 2;

		private static readonly Color TextOnline = new Color(0.45f, 0.95f, 0.45f, 1f);

		private static readonly Color TextOffline = new Color(1f, 0.4f, 0.4f, 1f);

		private const float AnimatorNormalBrightness = 0.757f;

		private const float UnknownDim = 0.66f;

		private static readonly Color DotOnline = new Color(0.35f, 0.85f, 0.35f, 1f);

		private static readonly Color DotOffline = new Color(0.9f, 0.3f, 0.3f, 1f);

		private static readonly Color DotUnknown = new Color(0.6f, 0.6f, 0.6f, 1f);

		private const float DotSize = 14f;

		private const float DotGap = 18f;

		private static Sprite circleSprite;

		private TMP_Text label;

		private Image dot;

		private RectTransform dotRect;

		private float lastDotX = float.NaN;

		private float nextCheck;

		private int lastLoggedStatus = -1;

		private bool addressWarned;

		private Color? lastAppliedColor;

		private Color lastBaseColor;

		private volatile int status;

		private volatile bool queryRunning;

		private volatile string failureReason;

		private volatile string onlineVia;

		private volatile bool needPlayFab;

		private volatile IPAddress resolvedAddress;

		private volatile int resolvedGamePort;

		private bool playFabRunning;

		private float playFabDeadline;

		private const float PlayFabTimeout = 20f;

		private static float OfflineInterval
		{
			get
			{
				if (!ServerConnectPlugin.PlayFabFallback.Value)
				{
					return ServerConnectPlugin.StatusCheckInterval.Value;
				}
				return Mathf.Max(10f, ServerConnectPlugin.StatusCheckInterval.Value);
			}
		}

		private static float OnlineInterval => ServerConnectPlugin.StatusCheckIntervalOnline.Value;

		public void Init(TMP_Text targetLabel)
		{
			label = targetLabel;
			if (ServerConnectPlugin.ServerStatusDot.Value)
			{
				CreateDot();
			}
			ApplyVisuals();
		}

		private void OnEnable()
		{
			nextCheck = 0f;
		}

		private void Update()
		{
			LogStatusChange();
			if (needPlayFab)
			{
				needPlayFab = false;
				StartPlayFabLookup();
				return;
			}
			if (playFabRunning && Time.unscaledTime > playFabDeadline)
			{
				FinishPlayFab(online: false, "PlayFab lookup timed out");
			}
			if (queryRunning || playFabRunning || Time.unscaledTime < nextCheck)
			{
				return;
			}
			if (!TryGetTarget(out var host, out var port, out var gamePort))
			{
				if (!addressWarned)
				{
					addressWarned = true;
					ServerConnectPlugin.Log.LogWarning((object)("[ServerConnect] Status check skipped - could not parse Address '" + ServerConnectPlugin.ServerButtonAddress.Value + "' (expected ip:port)."));
				}
				nextCheck = Time.unscaledTime + OfflineInterval;
				return;
			}
			nextCheck = Time.unscaledTime + ((status == 1) ? OnlineInterval : OfflineInterval);
			queryRunning = true;
			ThreadPool.QueueUserWorkItem(delegate
			{
				bool flag = false;
				string failure = null;
				IPAddress resolvedIp = null;
				try
				{
					flag = QueryA2S(host, port, out failure, out resolvedIp);
				}
				finally
				{
					failureReason = (flag ? null : failure);
					if (flag)
					{
						onlineVia = "Steam query";
						status = 1;
					}
					else if (ServerConnectPlugin.PlayFabFallback.Value && resolvedIp != null)
					{
						resolvedAddress = resolvedIp;
						resolvedGamePort = gamePort;
						needPlayFab = true;
					}
					else
					{
						status = 2;
					}
					queryRunning = false;
				}
			});
		}

		private void LateUpdate()
		{
			ApplyVisuals();
			PositionDot();
		}

		private void ApplyVisuals()
		{
			//IL_003e: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			ApplyLabelColor();
			if ((Object)(object)dot != (Object)null)
			{
				((Graphic)dot).color = ((status == 1) ? DotOnline : ((status == 2) ? DotOffline : DotUnknown));
			}
		}

		private void ApplyLabelColor()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_0088: 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_00bb: 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_00d7: 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_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)label == (Object)null) && ServerConnectPlugin.StatusColorText.Value)
			{
				Color color = ((Graphic)label).color;
				Color val = ((lastAppliedColor.HasValue && color == lastAppliedColor.Value) ? lastBaseColor : color);
				Color val3 = default(Color);
				if (status == 1 || status == 2)
				{
					Color val2 = ((status == 1) ? TextOnline : TextOffline);
					float num = Mathf.Max(val.r, Mathf.Max(val.g, val.b)) / 0.757f;
					num = Mathf.Clamp(num, 0.6f, 1.4f);
					((Color)(ref val3))..ctor(Mathf.Clamp01(val2.r * num), Mathf.Clamp01(val2.g * num), Mathf.Clamp01(val2.b * num), val.a);
				}
				else
				{
					((Color)(ref val3))..ctor(val.r * 0.66f, val.g * 0.66f, val.b * 0.66f, val.a);
				}
				((Graphic)label).color = val3;
				lastBaseColor = val;
				lastAppliedColor = val3;
			}
		}

		private static Sprite GetCircleSprite()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_00c5: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)circleSprite != (Object)null)
			{
				return circleSprite;
			}
			Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false);
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			float num = 15f;
			for (int i = 0; i < 32; i++)
			{
				for (int j = 0; j < 32; j++)
				{
					float num2 = (float)j + 0.5f - 16f;
					float num3 = (float)i + 0.5f - 16f;
					float num4 = Mathf.Clamp01(num - Mathf.Sqrt(num2 * num2 + num3 * num3) + 0.5f);
					val.SetPixel(j, i, new Color(1f, 1f, 1f, num4));
				}
			}
			val.Apply();
			circleSprite = Sprite.Create(val, new Rect(0f, 0f, 32f, 32f), new Vector2(0.5f, 0.5f), 100f);
			return circleSprite;
		}

		private void CreateDot()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("ServerStatusDot", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(LayoutElement)
			});
			val.transform.SetParent(((Component)this).transform, false);
			val.GetComponent<LayoutElement>().ignoreLayout = true;
			dotRect = val.GetComponent<RectTransform>();
			RectTransform obj = dotRect;
			RectTransform obj2 = dotRect;
			RectTransform obj3 = dotRect;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			obj3.pivot = val2;
			Vector2 anchorMin = (obj2.anchorMax = val2);
			obj.anchorMin = anchorMin;
			dotRect.sizeDelta = new Vector2(14f, 14f);
			dot = val.GetComponent<Image>();
			dot.sprite = GetCircleSprite();
			((Graphic)dot).raycastTarget = false;
			((Graphic)dot).color = DotUnknown;
		}

		private void PositionDot()
		{
			//IL_002e: 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_0038: 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_0075: 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)
			if (!((Object)(object)dotRect == (Object)null) && !((Object)(object)label == (Object)null))
			{
				Vector3 val = ((Component)this).transform.InverseTransformPoint(label.transform.position);
				float num = val.x - label.preferredWidth / 2f - 18f;
				if (!Mathf.Approximately(num, lastDotX))
				{
					lastDotX = num;
					dotRect.anchoredPosition = new Vector2(num, val.y);
				}
			}
		}

		private void LogStatusChange()
		{
			int num = status;
			if (num != 0 && num != lastLoggedStatus)
			{
				lastLoggedStatus = num;
				ServerConnectPlugin.Log.LogInfo((object)("[ServerConnect] Server status for '" + ServerConnectPlugin.ServerButtonAddress.Value + "': " + ((num == 1) ? ("online (" + (onlineVia ?? "?") + ")") : ("offline / not answering queries (" + (failureReason ?? "no details") + ")")) + "."));
			}
		}

		private unsafe void StartPlayFabLookup()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00a9: Expected O, but got Unknown
			try
			{
				IPAddress iPAddress = resolvedAddress;
				if (iPAddress == null || (Object)(object)PlayFabManager.instance == (Object)null || !PlayFabManager.IsLoggedIn || ZPlayFabMatchmaking.instance == null)
				{
					FinishPlayFab(online: false, "PlayFab not available (not logged in)");
					return;
				}
				playFabRunning = true;
				playFabDeadline = Time.unscaledTime + 20f;
				byte[] addressBytes = iPAddress.GetAddressBytes();
				IPv6Address val = default(IPv6Address);
				((IPv6Address)(ref val))..ctor((ushort)0, (ushort)0, (ushort)0, (ushort)0, (ushort)0, ushort.MaxValue, (ushort)((addressBytes[0] << 8) | addressBytes[1]), (ushort)((addressBytes[2] << 8) | addressBytes[3]));
				ZPlayFabMatchmaking.FindHostByIp(new IPEndPoint(val, (ushort)resolvedGamePort), (ZPlayFabMatchmakingSuccessCallback)delegate(PlayFabMatchmakingServerData result)
				{
					if (playFabRunning)
					{
						FinishPlayFab(result != null, (result != null) ? null : "not registered in PlayFab");
					}
				}, (ZPlayFabMatchmakingFailedCallback)delegate(ZPLayFabMatchmakingFailReason reason)
				{
					if (playFabRunning)
					{
						FinishPlayFab(online: false, "PlayFab: " + ((object)(*(ZPLayFabMatchmakingFailReason*)(&reason))/*cast due to .constrained prefix*/).ToString());
					}
				}, false);
			}
			catch (Exception ex)
			{
				playFabRunning = false;
				FinishPlayFab(online: false, "PlayFab lookup failed: " + ex.GetType().Name);
			}
		}

		private void FinishPlayFab(bool online, string detail)
		{
			playFabRunning = false;
			if (online)
			{
				failureReason = null;
				onlineVia = "PlayFab crossplay lookup";
				status = 1;
			}
			else
			{
				failureReason = (failureReason ?? "no Steam answer") + "; " + (detail ?? "PlayFab: no result");
				status = 2;
			}
		}

		private static bool TryGetTarget(out string host, out int port, out int gamePort)
		{
			host = null;
			port = 0;
			gamePort = 2456;
			string text = ServerConnectPlugin.ServerButtonAddress.Value.Trim();
			int num = text.LastIndexOf(':');
			if (num > 0)
			{
				if (!int.TryParse(text.Substring(num + 1), out gamePort))
				{
					return false;
				}
				host = text.Substring(0, num);
			}
			else
			{
				host = text;
			}
			if (string.IsNullOrEmpty(host))
			{
				return false;
			}
			int value = ServerConnectPlugin.ServerQueryPort.Value;
			port = ((value > 0) ? value : (gamePort + 1));
			if (port > 0)
			{
				return port <= 65535;
			}
			return false;
		}

		private static byte[] BuildRequest()
		{
			byte[] bytes = Encoding.ASCII.GetBytes("Source Engine Query\0");
			byte[] array = new byte[5 + bytes.Length];
			byte b;
			array[3] = (b = byte.MaxValue);
			array[0] = (array[1] = (array[2] = b));
			array[4] = 84;
			Buffer.BlockCopy(bytes, 0, array, 5, bytes.Length);
			return array;
		}

		private static bool IsQueryReply(byte[] data)
		{
			if (data == null || data.Length < 5)
			{
				return false;
			}
			bool num = data[0] == byte.MaxValue && data[1] == byte.MaxValue && data[2] == byte.MaxValue && data[3] == byte.MaxValue;
			bool flag = data[0] == byte.MaxValue && data[1] == byte.MaxValue && data[2] == byte.MaxValue && data[3] == 254;
			return num || flag;
		}

		private static bool QueryA2S(string host, int port, out string failure, out IPAddress resolvedIp)
		{
			failure = null;
			resolvedIp = null;
			try
			{
				IPAddress iPAddress = null;
				IPAddress[] hostAddresses = Dns.GetHostAddresses(host);
				foreach (IPAddress iPAddress2 in hostAddresses)
				{
					if (iPAddress2.AddressFamily == AddressFamily.InterNetwork)
					{
						iPAddress = iPAddress2;
						break;
					}
				}
				if (iPAddress == null)
				{
					failure = "could not resolve an IPv4 address for '" + host + "'";
					return false;
				}
				resolvedIp = iPAddress;
				IPEndPoint endPoint = new IPEndPoint(iPAddress, port);
				byte[] array = BuildRequest();
				for (int j = 1; j <= 2; j++)
				{
					try
					{
						using UdpClient udpClient = new UdpClient(AddressFamily.InterNetwork);
						udpClient.Client.ReceiveTimeout = 2500;
						udpClient.Send(array, array.Length, endPoint);
						IPEndPoint remoteEP = new IPEndPoint(IPAddress.Any, 0);
						byte[] array2 = udpClient.Receive(ref remoteEP);
						if (IsQueryReply(array2))
						{
							return true;
						}
						failure = "unexpected reply from " + remoteEP?.ToString() + " (" + array2.Length + " bytes, starts with " + BitConverter.ToString(array2, 0, Math.Min(array2.Length, 6)) + ")";
					}
					catch (SocketException ex)
					{
						failure = ((ex.SocketErrorCode == SocketError.TimedOut) ? ("no answer from " + iPAddress?.ToString() + ":" + port + " within " + 2500 + " ms (tried " + j + "x)") : (ex.SocketErrorCode.ToString() + " (" + ex.Message + ")"));
					}
				}
				return false;
			}
			catch (Exception ex2)
			{
				failure = ex2.GetType().Name + ": " + ex2.Message;
				return false;
			}
		}
	}
}