Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ServerConnect v1.0.14
ServerConnect.dll
Decompiled 3 weeks agousing 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; } } } }