Decompiled source of Valheim ServerGuard Client v1.8.1

Valheim-ServerGuard-Client.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;
using ValheimServerGuard.Shared;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("yesu0725")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Valheim ServerGuard Client - companion plugin that attests the client's mod list to a ServerGuard-protected server")]
[assembly: AssemblyFileVersion("1.8.1.0")]
[assembly: AssemblyInformationalVersion("1.8.1")]
[assembly: AssemblyProduct("Valheim-ServerGuard-Client")]
[assembly: AssemblyTitle("Valheim-ServerGuard-Client")]
[assembly: AssemblyVersion("1.8.1.0")]
namespace ValheimServerGuard.Shared
{
	public static class ModsetFingerprint
	{
		public static string ComputeStrict(IEnumerable<KeyValuePair<string, string>> entries)
		{
			return Sha256Hex(Canonicalize(entries, withHash: true));
		}

		public static string ComputeLoose(IEnumerable<KeyValuePair<string, string>> entries)
		{
			return Sha256Hex(Canonicalize(entries, withHash: false));
		}

		public static string Short(string fullHex)
		{
			if (!string.IsNullOrEmpty(fullHex) && fullHex.Length >= 8)
			{
				return fullHex.Substring(0, 8);
			}
			return fullHex ?? "";
		}

		private static string Canonicalize(IEnumerable<KeyValuePair<string, string>> entries, bool withHash)
		{
			if (entries == null)
			{
				return "";
			}
			List<string> list = (from e in entries
				where !string.IsNullOrWhiteSpace(e.Key)
				select (!withHash) ? (e.Key ?? "").ToLowerInvariant() : ((e.Key ?? "").ToLowerInvariant() + "|" + (e.Value ?? "").ToLowerInvariant())).Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>((string s) => s, StringComparer.Ordinal).ToList();
			if (list.Count != 0)
			{
				return string.Join("\n", list);
			}
			return "";
		}

		private static string Sha256Hex(string input)
		{
			using SHA256 sHA = SHA256.Create();
			return BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(input ?? ""))).Replace("-", "").ToLowerInvariant();
		}
	}
	[Serializable]
	public class ModManifestEntry
	{
		public string Guid;

		public string Name;

		public string Version;

		public string Sha256;
	}
	[Serializable]
	public class ModManifest
	{
		public string SchemaVersion = "1";

		public string Challenge;

		public long TimestampUtc;

		public List<ModManifestEntry> Mods = new List<ModManifestEntry>();

		public string Hmac;

		public string CanonicalForHmac()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(SchemaVersion ?? "").Append('|');
			stringBuilder.Append(Challenge ?? "").Append('|');
			stringBuilder.Append(TimestampUtc).Append('|');
			List<ModManifestEntry> list = new List<ModManifestEntry>(Mods ?? new List<ModManifestEntry>());
			list.Sort(delegate(ModManifestEntry a, ModManifestEntry b)
			{
				string strA = ((!string.IsNullOrEmpty(a?.Guid)) ? a.Guid : (a?.Name ?? ""));
				string strB = ((!string.IsNullOrEmpty(b?.Guid)) ? b.Guid : (b?.Name ?? ""));
				return string.CompareOrdinal(strA, strB);
			});
			foreach (ModManifestEntry item in list)
			{
				stringBuilder.Append(item?.Guid ?? "").Append(':');
				stringBuilder.Append(item?.Name ?? "").Append(':');
				stringBuilder.Append(item?.Version ?? "").Append(':');
				stringBuilder.Append(item?.Sha256 ?? "").Append(';');
			}
			return stringBuilder.ToString();
		}

		public static string ComputeHmac(string canonical, string secret)
		{
			if (string.IsNullOrEmpty(secret))
			{
				return "";
			}
			using HMACSHA256 hMACSHA = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
			return Convert.ToBase64String(hMACSHA.ComputeHash(Encoding.UTF8.GetBytes(canonical ?? "")));
		}

		public static bool ConstantTimeEquals(string a, string b)
		{
			if (a == null || b == null)
			{
				return false;
			}
			if (a.Length != b.Length)
			{
				return false;
			}
			int num = 0;
			for (int i = 0; i < a.Length; i++)
			{
				num |= a[i] ^ b[i];
			}
			return num == 0;
		}
	}
}
namespace ValheimServerGuardClient
{
	[BepInPlugin("com.taeguk.valheim.serverguard.client", "Valheim ServerGuard Client", "1.8.1")]
	public class ClientPlugin : BaseUnityPlugin
	{
		private class ClientSettings
		{
			public string SharedSecret { get; set; } = "";

			public bool QuickLoginEnabled { get; set; }

			public string ServerAddress { get; set; } = "";

			public int ServerPort { get; set; } = 2456;

			public string ServerPassword { get; set; } = "";

			public string ServerName { get; set; } = "";

			public string ServerDescription { get; set; } = "";

			public string ServerLogoPath { get; set; } = "";

			public string ServerAnnouncements { get; set; } = "";
		}

		[HarmonyPatch(typeof(Player), "PlacePiece")]
		public static class Patch_PlacePiece_Report
		{
			public static void Postfix(Player __instance, Piece piece, Vector3 pos)
			{
				//IL_007b: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					if (IsActiveMultiplayerClient() && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)piece == (Object)null))
					{
						GameObject gameObject = ((Component)piece).gameObject;
						string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "unknown";
						int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
						if (num > 0)
						{
							text = text.Substring(0, num).Trim();
						}
						Instance?.SendBuildPlace(text, pos);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] PlacePiece hook error: " + ex.Message));
					}
				}
			}
		}

		private sealed class LastHitInfo
		{
			public Character Attacker;

			public DateTime At;
		}

		[HarmonyPatch(typeof(WearNTear), "Damage")]
		public static class Patch_WearNTear_Damage_TrackClient
		{
			public static void Prefix(WearNTear __instance, HitData hit)
			{
				try
				{
					if (IsActiveMultiplayerClient() && !((Object)(object)__instance == (Object)null) && hit != null)
					{
						Character attacker = null;
						try
						{
							attacker = hit.GetAttacker();
						}
						catch
						{
						}
						LastHitInfo value = new LastHitInfo
						{
							Attacker = attacker,
							At = DateTime.UtcNow
						};
						_clientLastHitOnPiece.Remove(__instance);
						_clientLastHitOnPiece.Add(__instance, value);
					}
				}
				catch
				{
				}
			}
		}

		[HarmonyPatch(typeof(WearNTear), "Destroy")]
		public static class Patch_WearNTear_Destroy_ClientReport
		{
			public static void Prefix(WearNTear __instance)
			{
				//IL_0188: Unknown result type (might be due to invalid IL or missing references)
				//IL_005c: 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)
				try
				{
					if (!IsActiveMultiplayerClient() || (Object)(object)__instance == (Object)null)
					{
						return;
					}
					GameObject gameObject = ((Component)__instance).gameObject;
					string text = ((gameObject != null) ? ((Object)gameObject).name : null) ?? "unknown";
					int num = text.IndexOf("(Clone)", StringComparison.Ordinal);
					if (num > 0)
					{
						text = text.Substring(0, num).Trim();
					}
					Vector3 position;
					try
					{
						position = ((Component)__instance).transform.position;
					}
					catch
					{
						return;
					}
					string text2 = "unknown";
					string text3 = "";
					if (_clientLastHitOnPiece.TryGetValue(__instance, out var value) && value != null)
					{
						_clientLastHitOnPiece.Remove(__instance);
						Character attacker = value.Attacker;
						if ((Object)(object)attacker != (Object)null)
						{
							Player val = (Player)(object)((attacker is Player) ? attacker : null);
							if (val != null)
							{
								if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
								{
									text2 = "self";
									text3 = "";
								}
								else
								{
									text2 = "player";
									try
									{
										text3 = val.GetPlayerName() ?? "";
									}
									catch
									{
										text3 = "";
									}
								}
							}
							else
							{
								text2 = "creature";
								try
								{
									text3 = attacker.GetHoverName() ?? ((Object)attacker).name ?? "";
								}
								catch
								{
									text3 = ((Object)attacker).name ?? "";
								}
								int num2 = text3.IndexOf("(Clone)", StringComparison.Ordinal);
								if (num2 > 0)
								{
									text3 = text3.Substring(0, num2).Trim();
								}
							}
						}
					}
					if (text2 == "unknown")
					{
						text2 = "self";
						text3 = "";
					}
					Instance?.SendBuildDestroy(text, position, text2, text3);
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] WearNTear.Destroy hook error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		public static class Patch_Player_OnDeath_Report
		{
			public static void Prefix(Player __instance)
			{
				try
				{
					if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && IsActiveMultiplayerClient())
					{
						Instance?.SendDeathReport(__instance);
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Death hook error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		public static class Patch_RegisterClientHandler
		{
			public static void Postfix(ZNetPeer peer)
			{
				try
				{
					if (peer == null || peer.m_rpc == null || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()))
					{
						return;
					}
					peer.m_rpc.Register<string>("ServerGuard_RequestManifest", (Action<ZRpc, string>)delegate(ZRpc rpc, string challenge)
					{
						try
						{
							string text = Instance.BuildManifestJson(challenge);
							rpc.Invoke("ServerGuard_Manifest", new object[1] { text });
							LogS.LogInfo((object)$"[ServerGuard.Client] Sent manifest ({text.Length} bytes, {Instance._cachedManifest?.Count ?? 0} mods).");
						}
						catch (Exception ex2)
						{
							LogS.LogError((object)("[ServerGuard.Client] Manifest send failed: " + ex2.Message));
						}
					});
					if ((Object)(object)Instance != (Object)null)
					{
						Instance._serverRpc = peer.m_rpc;
					}
					peer.m_rpc.Register<string>("ServerGuard_AdminCommandReply", (Action<ZRpc, string>)delegate(ZRpc rpc, string text)
					{
						try
						{
							Instance?.DisplayAdminReply(text);
						}
						catch (Exception ex2)
						{
							ManualLogSource logS2 = LogS;
							if (logS2 != null)
							{
								logS2.LogWarning((object)("[ServerGuard.Client] Admin reply display failed: " + ex2.Message));
							}
						}
					});
					peer.m_rpc.Register<string>("ServerGuard_RemoveItems", (Action<ZRpc, string>)delegate(ZRpc rpc, string itemList)
					{
						try
						{
							Instance?.OnRemoveItemsReceived(itemList);
						}
						catch (Exception ex2)
						{
							ManualLogSource logS2 = LogS;
							if (logS2 != null)
							{
								logS2.LogWarning((object)("[ServerGuard.Client] RemoveItems handler error: " + ex2.Message));
							}
						}
					});
					peer.m_rpc.Register<string>("ServerGuard_ArrivalShout", (Action<ZRpc, string>)delegate(ZRpc rpc, string allowed)
					{
						try
						{
							OnArrivalShoutPolicyReceived(allowed);
						}
						catch (Exception ex2)
						{
							ManualLogSource logS2 = LogS;
							if (logS2 != null)
							{
								logS2.LogWarning((object)("[ServerGuard.Client] ArrivalShout handler error: " + ex2.Message));
							}
						}
					});
					peer.m_rpc.Register<string>("ServerGuard_ConsolePolicy", (Action<ZRpc, string>)delegate(ZRpc rpc, string payload)
					{
						try
						{
							OnConsolePolicyReceived(payload);
						}
						catch (Exception ex2)
						{
							ManualLogSource logS2 = LogS;
							if (logS2 != null)
							{
								logS2.LogWarning((object)("[ServerGuard.Client] ConsolePolicy handler error: " + ex2.Message));
							}
						}
					});
					LogS.LogInfo((object)"[ServerGuard.Client] Registered manifest request handler on server peer.");
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogError((object)("[ServerGuard.Client] Register handler failed: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Player), "StartEmote")]
		public static class Patch_Player_StartEmote_BlockDuringAttack
		{
			public static bool Prefix(Player __instance)
			{
				try
				{
					if (ShouldBlockAnimationCancel(__instance))
					{
						Instance?.ReportAnimationCancel("emote");
						return false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Emote gate error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Terminal), "updateBinds")]
		public static class Patch_Terminal_UpdateBinds
		{
			public static void Postfix()
			{
				try
				{
					ApplyBindPolicy();
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] updateBinds postfix error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		public static class Patch_ZNet_Shutdown_ResetPolicy
		{
			public static void Postfix()
			{
				try
				{
					if ((Object)(object)Instance != (Object)null)
					{
						Instance._serverRpc = null;
					}
					ResetConsolePolicy();
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Policy reset on shutdown failed: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Console), "IsConsoleEnabled")]
		public static class Patch_Console_IsConsoleEnabled
		{
			public static void Postfix(ref bool __result)
			{
				try
				{
					if (__result && !(_consoleMode != "disabled") && !ConsoleGuardExempt && IsActiveMultiplayerClient())
					{
						__result = false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Console lockout error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(Terminal), "TryRunCommand")]
		public static class Patch_TryRunCommand
		{
			public static bool Prefix(string text)
			{
				try
				{
					if (string.IsNullOrWhiteSpace(text))
					{
						return true;
					}
					string text2 = text.TrimStart(Array.Empty<char>());
					int num = text2.IndexOf(' ');
					string text3 = ((num >= 0) ? text2.Substring(0, num) : text2);
					string text4 = (text3.StartsWith("/", StringComparison.Ordinal) ? text3.Substring(1) : text3);
					if (string.Equals(text4, "sg", StringComparison.OrdinalIgnoreCase))
					{
						string command = ((num >= 0) ? text2.Substring(num + 1).TrimStart(Array.Empty<char>()) : "");
						Instance?.SendAdminCommand(command);
						return false;
					}
					if (!IsActiveMultiplayerClient())
					{
						return true;
					}
					if (ShouldBlockConsoleCommand(text4, out var category))
					{
						Instance?.ReportDevcommand(text4, category);
						NotifyBlocked(text4, category);
						return false;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Console gate error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Chat), "SendText")]
		public static class Patch_Chat_SendText_Report
		{
			public static bool Prefix(Type __0, string __1)
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				//IL_0022: Invalid comparison between Unknown and I4
				//IL_0073: Unknown result type (might be due to invalid IL or missing references)
				//IL_007a: Expected I4, but got Unknown
				try
				{
					if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
					{
						return true;
					}
					if ((int)__0 != 2)
					{
						return true;
					}
					if (_inRespawnUpdate && !_arrivalShoutAllowed && !_arrivalShoutConsumed)
					{
						_arrivalShoutConsumed = true;
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogInfo((object)"[ServerGuard.Client] Arrival shout suppressed (server policy).");
						}
						return false;
					}
					if (string.IsNullOrWhiteSpace(__1))
					{
						return true;
					}
					Instance?.SendChatReport((int)__0, __1);
				}
				catch (Exception ex)
				{
					ManualLogSource logS2 = LogS;
					if (logS2 != null)
					{
						logS2.LogWarning((object)("[ServerGuard.Client] Chat hook error: " + ex.Message));
					}
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(Game), "UpdateRespawn")]
		public static class Patch_Game_UpdateRespawn_ArrivalShout
		{
			public static void Prefix()
			{
				_inRespawnUpdate = true;
			}

			public static void Postfix()
			{
				_inRespawnUpdate = false;
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "SetupGui")]
		public static class Patch_FejdStartup_SetupGui
		{
			public static bool Prepare()
			{
				bool num = AccessTools.Method(typeof(FejdStartup), "SetupGui", (Type[])null, (Type[])null) != null;
				if (!num)
				{
					ManualLogSource logS = LogS;
					if (logS == null)
					{
						return num;
					}
					logS.LogWarning((object)"[ServerGuard.Client] FejdStartup.SetupGui not found — Quick Login panel disabled for this build.");
				}
				return num;
			}

			public static void Postfix(FejdStartup __instance)
			{
				try
				{
					Instance?.BuildQuickLoginPanel(__instance);
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] SetupGui patch error: " + ex.Message));
					}
				}
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "OnCharacterStart")]
		public static class Patch_FejdStartup_OnCharacterStart
		{
			public static bool Prepare()
			{
				return AccessTools.Method(typeof(FejdStartup), "OnCharacterStart", (Type[])null, (Type[])null) != null;
			}

			public static void Prefix(FejdStartup __instance)
			{
				ClientPlugin instance = Instance;
				if (!((Object)(object)instance == (Object)null) && instance._quickJoinArmed)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogInfo((object)"[ServerGuard.Client] OnCharacterStart: re-asserting quick-join target.");
					}
					instance.ReassertServerToJoin(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(FejdStartup), "OnSelelectCharacterBack")]
		public static class Patch_FejdStartup_CharacterBack
		{
			public static bool Prepare()
			{
				return AccessTools.Method(typeof(FejdStartup), "OnSelelectCharacterBack", (Type[])null, (Type[])null) != null;
			}

			public static void Postfix()
			{
				Instance?.DisarmQuickJoin();
			}
		}

		internal class AnnouncementLinkClicker : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
		{
			private Component _tmp;

			private Canvas _canvas;

			private static MethodInfo _finder;

			private static bool _finderResolved;

			internal void Init(Component tmp)
			{
				_tmp = tmp;
				_canvas = ((Component)this).GetComponentInParent<Canvas>();
			}

			public void OnPointerClick(PointerEventData eventData)
			{
				//IL_004e: 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_007d: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_tmp == (Object)null || eventData == null || eventData.dragging)
				{
					return;
				}
				try
				{
					MethodInfo methodInfo = ResolveFinder(((object)_tmp).GetType());
					if (methodInfo == null)
					{
						return;
					}
					Camera val = (((Object)(object)_canvas != (Object)null && (int)_canvas.renderMode != 0) ? _canvas.worldCamera : null);
					if (!(methodInfo.Invoke(null, new object[3]
					{
						_tmp,
						Vector2.op_Implicit(eventData.position),
						val
					}) is int num) || num < 0)
					{
						return;
					}
					string linkId = GetLinkId(_tmp, num);
					if (string.IsNullOrEmpty(linkId))
					{
						return;
					}
					if (!IsOpenableUrl(linkId))
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogWarning((object)("[ServerGuard.Client] Ignoring announcement link with an unsupported scheme: " + linkId));
						}
						return;
					}
					ManualLogSource logS2 = LogS;
					if (logS2 != null)
					{
						logS2.LogInfo((object)("[ServerGuard.Client] Opening announcement link: " + linkId));
					}
					Application.OpenURL(linkId);
				}
				catch (Exception ex)
				{
					ManualLogSource logS3 = LogS;
					if (logS3 != null)
					{
						logS3.LogWarning((object)("[ServerGuard.Client] Announcement link click failed: " + ex.Message));
					}
				}
			}

			private static MethodInfo ResolveFinder(Type tmpType)
			{
				if (_finderResolved)
				{
					return _finder;
				}
				_finderResolved = true;
				Type type = AppDomain.CurrentDomain.GetAssemblies().Select(delegate(Assembly a)
				{
					try
					{
						return a.GetType("TMPro.TMP_TextUtilities");
					}
					catch
					{
						return (Type)null;
					}
				}).FirstOrDefault((Type t) => t != null);
				if (type == null)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)"[ServerGuard.Client] TMP_TextUtilities not found - announcement links won't be clickable.");
					}
					return null;
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!(methodInfo.Name != "FindIntersectingLink"))
					{
						ParameterInfo[] parameters = methodInfo.GetParameters();
						if (parameters.Length == 3 && parameters[0].ParameterType.IsAssignableFrom(tmpType) && !(parameters[1].ParameterType != typeof(Vector3)) && !(parameters[2].ParameterType != typeof(Camera)))
						{
							_finder = methodInfo;
							break;
						}
					}
				}
				if (_finder == null)
				{
					ManualLogSource logS2 = LogS;
					if (logS2 != null)
					{
						logS2.LogWarning((object)"[ServerGuard.Client] FindIntersectingLink(TMP_Text, Vector3, Camera) not found - announcement links won't be clickable.");
					}
				}
				return _finder;
			}

			private static string GetLinkId(Component tmp, int index)
			{
				object obj = GetTmpProperty(tmp, "textInfo") ?? ((object)tmp).GetType().GetField("m_textInfo", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(tmp);
				if (obj == null)
				{
					return null;
				}
				if (!(obj.GetType().GetField("linkInfo", BindingFlags.Instance | BindingFlags.Public)?.GetValue(obj) is Array array) || index >= array.Length)
				{
					return null;
				}
				object value = array.GetValue(index);
				return (value?.GetType().GetMethod("GetLinkID", BindingFlags.Instance | BindingFlags.Public))?.Invoke(value, null) as string;
			}
		}

		public const string GUID = "com.taeguk.valheim.serverguard.client";

		public const string NAME = "Valheim ServerGuard Client";

		public const string VERSION = "1.8.1";

		internal static ClientPlugin Instance;

		internal static ManualLogSource LogS;

		private Harmony _harmony;

		private string _sharedSecret = "";

		private List<ModManifestEntry> _cachedManifest;

		private ClientSettings _clientSettings = new ClientSettings();

		private GameObject _quickLoginPanel;

		private Component _playerCountText;

		private bool _quickJoinArmed;

		private object _armedJoinData;

		private string _armedPassword;

		internal ZRpc _serverRpc;

		private static readonly HashSet<string> CheatCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"devcommands", "debugmode", "imacheater", "god", "ghost", "heal", "puke", "damage", "addstatus", "clearstatus",
			"resetcharacter", "setpower", "model", "beard", "hair", "fly", "freefly", "goto", "findtp", "pos",
			"ffsmooth", "recall", "spawn", "itemset", "location", "nextseed", "genloc", "setfuel", "nocost", "noplacementcost",
			"forcedelete", "killall", "killenemies", "killtame", "removedrops", "removebirds", "removefish", "tame", "aggravate", "setkey",
			"removekey", "resetkeys", "listkeys", "event", "randomevent", "stopevent", "tod", "skiptime", "sleep", "timescale",
			"env", "resetenv", "wind", "resetwind", "players", "raiseskill", "resetskill", "exploremap", "resetmap", "find",
			"printcreatures", "printlocations", "dpsdebug", "gc", "test"
		};

		private static readonly HashSet<string> RiskyCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"nomap", "noportals", "setworldmodifier", "setworldpreset", "resetworldkeys", "resetsharedmap", "resetspawn", "optterrain", "printseeds", "resetknownitems",
			"resetplayerprefs", "cr", "restartparty"
		};

		private static readonly HashSet<string> BindCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "bind", "unbind", "resetbinds", "printbinds" };

		private static readonly HashSet<string> VanillaAdminCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "ban", "unban", "banned", "kick", "save" };

		private static readonly HashSet<string> AlwaysAllowedCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"sg", "help", "clear", "info", "ping", "fov", "maxfps", "exclusivefullscreen", "hidebetatext", "sortcraft",
			"filtercraft", "tutorialtoggle", "tutorialreset", "xb:version", "s", "say", "w", "die", "respawn", "wave",
			"sit", "challenge", "cheer", "nonono", "thumbsup", "point", "blowkiss", "bow", "cower", "cry",
			"despair", "flex", "comehere", "headbang", "kneel", "laugh", "roar", "shrug", "dance", "relax",
			"toast", "rest", "vibe", "loveyou", "count"
		};

		private static string _consoleMode = "restricted";

		private static string _consoleBindPolicy = "allow";

		private static string _consoleRole = "player";

		private static HashSet<string> _consoleExtraBlocked = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static HashSet<string> _consoleAllowed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static bool _consoleExempt = false;

		private static readonly string ConfDir = Path.Combine(Paths.ConfigPath, "ServerGuard");

		private static readonly string ClientYaml = Path.Combine(ConfDir, "client.yaml");

		private static readonly string ExportYaml = Path.Combine(ConfDir, "mods_for_allowed_mods.yaml");

		private static MethodInfo _consoleWriteMethod;

		private static int _consoleWriteArity;

		private static object _consoleInstance;

		private static readonly ConditionalWeakTable<WearNTear, LastHitInfo> _clientLastHitOnPiece = new ConditionalWeakTable<WearNTear, LastHitInfo>();

		private static FieldInfo _playerLastHitField;

		private static MethodInfo _hitGetAttackerMethod;

		private static FieldInfo _hitDamageField;

		private const float SkillReportIntervalSeconds = 60f;

		private static FieldInfo _playerSkillsField;

		private static FieldInfo _skillsDataField;

		private static FieldInfo _skillLevelField;

		private static IDictionary _terminalCommands;

		private static bool _terminalCommandsResolved;

		private static FieldInfo _bindsField;

		private static FieldInfo _bindListField;

		private static bool _bindFieldsResolved;

		private static bool _arrivalShoutAllowed = true;

		private static bool _inRespawnUpdate;

		private static bool _arrivalShoutConsumed;

		private const float AnnHeaderBlock = 26f;

		private const float AnnMinViewport = 140f;

		private const float AnnBottomStack = 100f;

		private const float AnnScrollbarW = 8f;

		private const float AnnTextPad = 6f;

		private static readonly Regex AnnLinkRegex = new Regex("\\[([^\\]\\r\\n]+)\\]\\(\\s*([^)\\s]+)\\s*\\)");

		private static bool ConsoleGuardExempt => _consoleExempt;

		internal static bool IsOwnerClient => _consoleRole == "owner";

		internal static void OnConsolePolicyReceived(string payload)
		{
			try
			{
				string[] array = (payload ?? "").Split(new char[1] { '|' });
				if (array.Length < 4)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)$"[ServerGuard.Client] Malformed console policy payload ({array.Length} fields) - keeping current policy.");
					}
					return;
				}
				_consoleMode = array[0].Trim().ToLowerInvariant();
				_consoleExempt = array[1].Trim() == "1";
				_consoleRole = array[2].Trim().ToLowerInvariant();
				_consoleBindPolicy = array[3].Trim().ToLowerInvariant();
				_consoleExtraBlocked = ToSet((array.Length > 4) ? array[4] : "");
				_consoleAllowed = ToSet((array.Length > 5) ? array[5] : "");
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogInfo((object)("[ServerGuard.Client] Console policy: mode=" + _consoleMode + " binds=" + _consoleBindPolicy + " " + $"role={_consoleRole} exempt={_consoleExempt} " + $"extraBlocked={_consoleExtraBlocked.Count} allowed={_consoleAllowed.Count}"));
				}
				ApplyBindPolicy();
			}
			catch (Exception ex)
			{
				ManualLogSource logS3 = LogS;
				if (logS3 != null)
				{
					logS3.LogWarning((object)("[ServerGuard.Client] Console policy parse failed: " + ex.Message));
				}
			}
		}

		private static HashSet<string> ToSet(string csv)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = (csv ?? "").Split(new char[1] { ',' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0)
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}

		internal static void ResetConsolePolicy()
		{
			_consoleMode = "restricted";
			_consoleBindPolicy = "allow";
			_consoleRole = "player";
			_consoleExempt = false;
			_consoleExtraBlocked = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			_consoleAllowed = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		}

		private void Awake()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Instance = this;
			LogS = ((BaseUnityPlugin)this).Logger;
			EnsureConfig();
			_harmony = new Harmony("com.taeguk.valheim.serverguard.client");
			_harmony.PatchAll();
			((MonoBehaviour)this).StartCoroutine(DeferredInit());
		}

		private IEnumerator DeferredInit()
		{
			yield return null;
			yield return (object)new WaitForSeconds(2f);
			BuildManifestCache();
			ExportAllowedModsSnippet();
			((MonoBehaviour)this).StartCoroutine(SkillReportLoop());
			string text = "";
			string text2 = "";
			try
			{
				List<KeyValuePair<string, string>> entries = (_cachedManifest ?? new List<ModManifestEntry>()).Select((ModManifestEntry m) => new KeyValuePair<string, string>((!string.IsNullOrEmpty(m.Guid)) ? m.Guid : (m.Name ?? ""), m.Sha256 ?? "")).ToList();
				text = ModsetFingerprint.Short(ModsetFingerprint.ComputeLoose(entries));
				text2 = ModsetFingerprint.Short(ModsetFingerprint.ComputeStrict(entries));
			}
			catch (Exception ex)
			{
				LogS.LogWarning((object)("[ServerGuard.Client] Fingerprint compute failed: " + ex.Message));
			}
			LogS.LogInfo((object)string.Format("[ServerGuard.Client] Loaded v{0}. Manifest entries: {1}. HMAC: {2}", "1.8.1", _cachedManifest?.Count ?? 0, string.IsNullOrEmpty(_sharedSecret) ? "OFF (no shared_secret configured)" : "ON"));
			LogS.LogInfo((object)("[ServerGuard.Client] Modset fingerprint  loose=" + text + "  strict=" + text2));
		}

		private void ExportAllowedModsSnippet()
		{
			try
			{
				if (File.Exists(ExportYaml))
				{
					LogS.LogInfo((object)("[ServerGuard.Client] Allowed-mods export already present at " + ExportYaml + ". Delete the file to regenerate."));
					return;
				}
				List<ModManifestEntry> list = _cachedManifest ?? new List<ModManifestEntry>();
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("# ServerGuard - allowed_mods snippet generated by ServerGuard.Client v1.8.1");
				stringBuilder.AppendLine($"# Generated: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss}Z   Mods on this client: {list.Count}");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# How to use:");
				stringBuilder.AppendLine("#   1. Open <server>/BepInEx/config/ServerGuard/conf/allowed_mods.yaml");
				stringBuilder.AppendLine("#   2. Replace the `allowed_mods:` block with the one below");
				stringBuilder.AppendLine("#      (or merge if you already have entries you want to keep).");
				stringBuilder.AppendLine("#   3. Save. The server hot-reloads within ~1 second.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# Each entry is `<GUID>|<sha256>` (GUID-keyed, hash-pinned).");
				stringBuilder.AppendLine("# To loosen, drop the `|<sha256>` suffix - the entry will then accept any hash.");
				stringBuilder.AppendLine("# To tighten further, leave it as-is - the server will require an exact DLL match.");
				stringBuilder.AppendLine("#");
				stringBuilder.AppendLine("# The companion plugin (this DLL) is intentionally listed under required_mods,");
				stringBuilder.AppendLine("# NOT allowed_mods - the server demands its presence.");
				stringBuilder.AppendLine();
				ModManifestEntry modManifestEntry = list.FirstOrDefault((ModManifestEntry m) => string.Equals(m.Guid, "com.taeguk.valheim.serverguard.client", StringComparison.OrdinalIgnoreCase));
				stringBuilder.AppendLine("required_mods:");
				if (modManifestEntry != null && !string.IsNullOrEmpty(modManifestEntry.Sha256))
				{
					stringBuilder.AppendLine("  - " + modManifestEntry.Guid + "|" + modManifestEntry.Sha256 + "    # " + modManifestEntry.Name + " v" + modManifestEntry.Version);
				}
				else
				{
					stringBuilder.AppendLine("  - com.taeguk.valheim.serverguard.client                                                # Valheim ServerGuard Client v1.8.1");
				}
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("allowed_mods:");
				List<ModManifestEntry> list2 = list.Where((ModManifestEntry m) => !string.Equals(m.Guid, "com.taeguk.valheim.serverguard.client", StringComparison.OrdinalIgnoreCase)).OrderBy<ModManifestEntry, string>((ModManifestEntry m) => m.Name ?? "", StringComparer.OrdinalIgnoreCase).ToList();
				if (list2.Count == 0)
				{
					stringBuilder.AppendLine("  []");
				}
				else
				{
					int num = 0;
					foreach (ModManifestEntry item in list2)
					{
						int num2 = (((!string.IsNullOrEmpty(item.Guid)) ? item.Guid : item.Name) ?? "").Length + ((!string.IsNullOrEmpty(item.Sha256)) ? (1 + item.Sha256.Length) : 0);
						if (num2 > num)
						{
							num = num2;
						}
					}
					foreach (ModManifestEntry item2 in list2)
					{
						string text = ((!string.IsNullOrEmpty(item2.Guid)) ? item2.Guid : (item2.Name ?? ""));
						string text2 = (string.IsNullOrEmpty(item2.Sha256) ? text : (text + "|" + item2.Sha256));
						string text3 = new string(' ', Math.Max(1, num - text2.Length + 2));
						string text4 = (string.IsNullOrEmpty(item2.Name) ? "" : (item2.Name + " v" + item2.Version));
						if (string.IsNullOrEmpty(item2.Guid))
						{
							stringBuilder.AppendLine("  - " + text2 + text3 + "# " + text4 + " (no GUID; consider replacing the key with the mod's BepInPlugin GUID)");
						}
						else
						{
							stringBuilder.AppendLine("  - " + text2 + text3 + "# " + text4);
						}
					}
				}
				stringBuilder.AppendLine();
				stringBuilder.AppendLine("banned_mods: []");
				stringBuilder.AppendLine();
				Directory.CreateDirectory(ConfDir);
				File.WriteAllText(ExportYaml, stringBuilder.ToString());
				LogS.LogWarning((object)"[ServerGuard.Client] First-run mod export written:");
				LogS.LogWarning((object)("[ServerGuard.Client]   " + ExportYaml));
				LogS.LogWarning((object)$"[ServerGuard.Client]   ({list.Count} plugins). Paste its contents into the server's allowed_mods.yaml.");
			}
			catch (Exception ex)
			{
				LogS.LogError((object)("[ServerGuard.Client] ExportAllowedModsSnippet failed: " + ex.Message));
			}
		}

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

		private void EnsureConfig()
		{
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Expected O, but got Unknown
			try
			{
				Directory.CreateDirectory(ConfDir);
				if (!File.Exists(ClientYaml))
				{
					StringBuilder stringBuilder = new StringBuilder();
					stringBuilder.AppendLine("# Valheim ServerGuard - Client config");
					stringBuilder.AppendLine("");
					stringBuilder.AppendLine("# sharedSecret MUST match the server's settings.yaml `sharedSecret` value");
					stringBuilder.AppendLine("# verbatim. The server will reject manifests whose HMAC does not match.");
					stringBuilder.AppendLine("# Leave empty only if the server has `requireHmac: false` (insecure).");
					stringBuilder.AppendLine("sharedSecret: \"\"");
					stringBuilder.AppendLine("");
					stringBuilder.AppendLine("# ---------------------------------------------------------------");
					stringBuilder.AppendLine("# Quick Login panel (title screen)");
					stringBuilder.AppendLine("# When enabled, a panel is shown on the main menu so players can");
					stringBuilder.AppendLine("# connect to your server with one click - no IP/password dialog.");
					stringBuilder.AppendLine("# ---------------------------------------------------------------");
					stringBuilder.AppendLine("quickLoginEnabled: false");
					stringBuilder.AppendLine("serverAddress: \"\"       # e.g. 192.168.1.1 or my.server.com");
					stringBuilder.AppendLine("serverPort: 2456");
					stringBuilder.AppendLine("serverPassword: \"\"     # stored in plain text; leave empty for public servers");
					stringBuilder.AppendLine("serverName: \"\"         # displayed as the panel heading");
					stringBuilder.AppendLine("serverDescription: \"\" # shown below the name");
					stringBuilder.AppendLine("serverLogoPath: \"\"    # PNG/JPG filename in BepInEx/config/ServerGuard/");
					stringBuilder.Append(AnnouncementsYamlBlock());
					File.WriteAllText(ClientYaml, stringBuilder.ToString());
				}
				else
				{
					string text = File.ReadAllText(ClientYaml);
					if (text.IndexOf("serverAnnouncements", StringComparison.OrdinalIgnoreCase) < 0)
					{
						if (!text.EndsWith("\n"))
						{
							text += Environment.NewLine;
						}
						File.WriteAllText(ClientYaml, text + AnnouncementsYamlBlock());
						LogS.LogInfo((object)"[ServerGuard.Client] Added the serverAnnouncements block to client.yaml.");
					}
				}
				ClientSettings clientSettings = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).IgnoreUnmatchedProperties().Build()
					.Deserialize<ClientSettings>(File.ReadAllText(ClientYaml)) ?? new ClientSettings();
				_sharedSecret = clientSettings.SharedSecret ?? "";
				_clientSettings = clientSettings;
			}
			catch (Exception ex)
			{
				LogS.LogWarning((object)("[ServerGuard.Client] EnsureConfig failed: " + ex.Message));
			}
		}

		private static string AnnouncementsYamlBlock()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("");
			stringBuilder.AppendLine("# ---------------------------------------------------------------");
			stringBuilder.AppendLine("# Announcements (scrollable box under the description)");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# Write as many lines as you like - the box scrolls (mouse wheel or");
			stringBuilder.AppendLine("# the scrollbar on its right edge). Leave empty to hide the section.");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# Links:   [label](https://example.com)  -> clickable, opens a browser.");
			stringBuilder.AppendLine("#          Only http:// and https:// links are opened.");
			stringBuilder.AppendLine("# Styling: <b>bold</b>, <i>italic</i>, <color=#ffcc00>colour</color>.");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("# To use it, replace the \"\" below with a `|` block and indent EVERY");
			stringBuilder.AppendLine("# line of text by two spaces:");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("#   serverAnnouncements: |");
			stringBuilder.AppendLine("#     <b>Welcome!</b>");
			stringBuilder.AppendLine("#     Server wipe: never. Raids: on.");
			stringBuilder.AppendLine("#");
			stringBuilder.AppendLine("#     Join our [Discord](https://discord.gg/example) for events.");
			stringBuilder.AppendLine("# ---------------------------------------------------------------");
			stringBuilder.AppendLine("serverAnnouncements: \"\"");
			return stringBuilder.ToString();
		}

		private void BuildManifestCache()
		{
			_cachedManifest = new List<ModManifestEntry>();
			try
			{
				foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos)
				{
					PluginInfo value = pluginInfo.Value;
					BepInPlugin val = ((value != null) ? value.Metadata : null);
					string sha = "";
					try
					{
						string text = ((value != null) ? value.Location : null);
						if (!string.IsNullOrEmpty(text) && File.Exists(text))
						{
							using SHA256 sHA = SHA256.Create();
							using FileStream inputStream = File.OpenRead(text);
							sha = BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", "").ToLowerInvariant();
						}
					}
					catch
					{
					}
					_cachedManifest.Add(new ModManifestEntry
					{
						Guid = (((val != null) ? val.GUID : null) ?? ""),
						Name = (((val != null) ? val.Name : null) ?? ""),
						Version = (((val == null) ? null : val.Version?.ToString()) ?? ""),
						Sha256 = sha
					});
				}
			}
			catch (Exception ex)
			{
				LogS.LogError((object)("[ServerGuard.Client] BuildManifestCache failed: " + ex.Message));
			}
		}

		public string BuildManifestJson(string challenge)
		{
			BuildManifestCache();
			ModManifest obj = new ModManifest
			{
				SchemaVersion = "1",
				Challenge = (challenge ?? ""),
				TimestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
				Mods = (_cachedManifest ?? new List<ModManifestEntry>())
			};
			obj.Hmac = ModManifest.ComputeHmac(obj.CanonicalForHmac(), _sharedSecret);
			return JsonConvert.SerializeObject((object)obj);
		}

		internal void SendAdminCommand(string command)
		{
			if (!IsActiveMultiplayerClient())
			{
				DisplayAdminReply("[ServerGuard] sg commands only work while connected to a multiplayer server.");
				return;
			}
			if (_serverRpc == null)
			{
				DisplayAdminReply("[ServerGuard] Not connected to a server peer yet.");
				return;
			}
			try
			{
				_serverRpc.Invoke("ServerGuard_AdminCommand", new object[1] { command ?? "" });
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogInfo((object)("[ServerGuard.Client] Sent admin command: " + command));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] Admin command send failed: " + ex.Message));
				}
				DisplayAdminReply("[ServerGuard] Send failed: " + ex.Message);
			}
		}

		internal void DisplayAdminReply(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array = text.Split(new char[1] { '\n' });
			try
			{
				object obj = ResolveConsoleInstance();
				string[] array2;
				if (obj == null || _consoleWriteMethod == null)
				{
					array2 = array;
					foreach (string text2 in array2)
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogInfo((object)("[ServerGuard] " + text2));
						}
					}
					return;
				}
				array2 = array;
				foreach (string text3 in array2)
				{
					if (string.IsNullOrWhiteSpace(text3))
					{
						continue;
					}
					try
					{
						object[] parameters = ((_consoleWriteArity != 1) ? new object[2] { text3, false } : new object[1] { text3 });
						_consoleWriteMethod.Invoke(obj, parameters);
					}
					catch
					{
						ManualLogSource logS2 = LogS;
						if (logS2 != null)
						{
							logS2.LogInfo((object)("[ServerGuard] " + text3));
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS3 = LogS;
				if (logS3 != null)
				{
					logS3.LogWarning((object)("[ServerGuard.Client] DisplayAdminReply error: " + ex.Message));
				}
			}
		}

		private object ResolveConsoleInstance()
		{
			if (_consoleInstance != null && _consoleWriteMethod != null)
			{
				return _consoleInstance;
			}
			Type type = typeof(Terminal).Assembly.GetType("Console");
			object obj = null;
			if (type != null)
			{
				try
				{
					PropertyInfo property = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null)
					{
						obj = property.GetValue(null);
					}
				}
				catch
				{
				}
				if (obj == null)
				{
					try
					{
						FieldInfo field = type.GetField("m_instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
						if (field != null)
						{
							obj = field.GetValue(null);
						}
					}
					catch
					{
					}
				}
			}
			if (obj == null)
			{
				try
				{
					obj = Object.FindObjectOfType<Terminal>();
				}
				catch
				{
				}
			}
			if (obj == null)
			{
				return null;
			}
			MethodInfo methodInfo = null;
			int num = 0;
			string[] array = new string[2] { "Print", "AddString" };
			foreach (string text in array)
			{
				MethodInfo[] methods = obj.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo2 in methods)
				{
					if (!(methodInfo2.Name != text))
					{
						ParameterInfo[] parameters = methodInfo2.GetParameters();
						if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
						{
							methodInfo = methodInfo2;
							num = 1;
							break;
						}
						if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType == typeof(bool) && methodInfo == null)
						{
							methodInfo = methodInfo2;
							num = 2;
						}
					}
				}
				if (methodInfo != null && num == 1)
				{
					break;
				}
			}
			if (methodInfo == null)
			{
				return null;
			}
			_consoleInstance = obj;
			_consoleWriteMethod = methodInfo;
			_consoleWriteArity = num;
			return _consoleInstance;
		}

		internal void SendBuildPlace(string pieceName, Vector3 pos)
		{
			//IL_0029: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				string text = SanitiseShort(pieceName, 64);
				string text2 = string.Format(CultureInfo.InvariantCulture, "{0}|{1:F1}|{2:F1}|{3:F1}", text, pos.x, pos.y, pos.z);
				_serverRpc.Invoke("ServerGuard_BuildPlace", new object[1] { text2 });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] BuildPlace RPC failed: " + ex.Message));
				}
			}
		}

		internal void SendBuildDestroy(string pieceName, Vector3 pos, string attackerKind, string attackerLabel)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: 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)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				string text = SanitiseShort(pieceName, 64);
				string text2 = SanitiseShort(attackerKind ?? "unknown", 16);
				string text3 = SanitiseShort(attackerLabel ?? "", 48);
				string text4 = string.Format(CultureInfo.InvariantCulture, "{0}|{1:F1}|{2:F1}|{3:F1}|{4}|{5}", text, pos.x, pos.y, pos.z, text2, text3);
				_serverRpc.Invoke("ServerGuard_BuildDestroy", new object[1] { text4 });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] BuildDestroy RPC failed: " + ex.Message));
				}
			}
		}

		private static string SanitiseShort(string s, int max)
		{
			string text = (s ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
			if (text.Length > max)
			{
				text = text.Substring(0, max);
			}
			return text;
		}

		internal void SendDeathReport(Player p)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			if (_serverRpc == null)
			{
				return;
			}
			try
			{
				Vector3 position = ((Component)p).transform.position;
				string text = "environment";
				string text2 = "";
				string text3 = "";
				if (_playerLastHitField == null)
				{
					FieldInfo[] fields = typeof(Player).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						if (fieldInfo.FieldType == typeof(HitData))
						{
							_playerLastHitField = fieldInfo;
							break;
						}
					}
				}
				object? obj = _playerLastHitField?.GetValue(p);
				HitData val = (HitData)((obj is HitData) ? obj : null);
				if (val != null && val != null)
				{
					text3 = DominantDamageType(val);
					if (_hitGetAttackerMethod == null)
					{
						_hitGetAttackerMethod = typeof(HitData).GetMethod("GetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					}
					Character val2 = null;
					if (_hitGetAttackerMethod != null)
					{
						try
						{
							object? obj2 = _hitGetAttackerMethod.Invoke(val, null);
							val2 = (Character)((obj2 is Character) ? obj2 : null);
						}
						catch
						{
						}
					}
					if ((Object)(object)val2 != (Object)null)
					{
						Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
						if (val3 != null)
						{
							if ((Object)(object)val3 == (Object)(object)p)
							{
								text = "self";
								text2 = "";
							}
							else
							{
								text = "player";
								text2 = val3.GetPlayerName() ?? "";
							}
						}
						else
						{
							text = "creature";
							try
							{
								text2 = val2.GetHoverName() ?? ((Object)val2).name ?? "";
							}
							catch
							{
								text2 = ((Object)val2).name ?? "";
							}
						}
					}
				}
				text2 = (text2 ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
				text3 = (text3 ?? "").Replace('|', ' ').Replace('\n', ' ').Trim();
				string text4 = string.Format(CultureInfo.InvariantCulture, "{0:F1}|{1:F1}|{2:F1}|{3}|{4}|{5}", position.x, position.y, position.z, text, text2, text3);
				try
				{
					_serverRpc.Invoke("ServerGuard_PlayerDeath", new object[1] { text4 });
					LogS.LogInfo((object)("[ServerGuard.Client] Death report sent (" + text + " / " + text2 + " / " + text3 + ")."));
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Death report RPC failed: " + ex.Message));
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] SendDeathReport error: " + ex2.Message));
				}
			}
		}

		private static string DominantDamageType(HitData hit)
		{
			if (hit == null)
			{
				return "";
			}
			FieldInfo[] fields;
			if (_hitDamageField == null)
			{
				fields = typeof(HitData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.Name.Equals("m_damage", StringComparison.OrdinalIgnoreCase))
					{
						_hitDamageField = fieldInfo;
						break;
					}
				}
			}
			if (_hitDamageField == null)
			{
				return "";
			}
			object value;
			try
			{
				value = _hitDamageField.GetValue(hit);
			}
			catch
			{
				return "";
			}
			if (value == null)
			{
				return "";
			}
			string text = "";
			float num = 0f;
			fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo2 in fields)
			{
				if (!(fieldInfo2.FieldType != typeof(float)))
				{
					float num2;
					try
					{
						num2 = (float)fieldInfo2.GetValue(value);
					}
					catch
					{
						continue;
					}
					if (num2 > num)
					{
						num = num2;
						text = fieldInfo2.Name;
					}
				}
			}
			if (text.StartsWith("m_", StringComparison.Ordinal))
			{
				text = text.Substring(2);
			}
			if (text.Length > 0)
			{
				text = char.ToUpperInvariant(text[0]) + text.Substring(1);
			}
			return text;
		}

		private IEnumerator SkillReportLoop()
		{
			yield return (object)new WaitForSeconds(15f);
			while (true)
			{
				yield return (object)new WaitForSeconds(60f);
				try
				{
					SendSkillReportNow();
				}
				catch (Exception ex)
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Skill report tick error: " + ex.Message));
					}
				}
			}
		}

		private static Skills GetPlayerSkills(Player p)
		{
			if ((Object)(object)p == (Object)null)
			{
				return null;
			}
			if (_playerSkillsField == null)
			{
				FieldInfo[] fields = typeof(Player).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType == typeof(Skills))
					{
						_playerSkillsField = fieldInfo;
						break;
					}
				}
				if (_playerSkillsField == null)
				{
					return null;
				}
			}
			object? value = _playerSkillsField.GetValue(p);
			return (Skills)((value is Skills) ? value : null);
		}

		private static IEnumerable<KeyValuePair<string, float>> EnumerateSkills(Skills skills)
		{
			if ((Object)(object)skills == (Object)null)
			{
				yield break;
			}
			if (_skillsDataField == null)
			{
				FieldInfo[] fields = typeof(Skills).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType))
					{
						_skillsDataField = fieldInfo;
						break;
					}
				}
				if (_skillsDataField == null)
				{
					yield break;
				}
			}
			if (!(_skillsDataField.GetValue(skills) is IDictionary dictionary))
			{
				yield break;
			}
			foreach (DictionaryEntry item in dictionary)
			{
				string text = item.Key?.ToString();
				if (string.IsNullOrEmpty(text))
				{
					continue;
				}
				object value = item.Value;
				if (value == null)
				{
					continue;
				}
				if (_skillLevelField == null)
				{
					FieldInfo[] fields = value.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo2 in fields)
					{
						if (fieldInfo2.FieldType == typeof(float) && fieldInfo2.Name.IndexOf("level", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							_skillLevelField = fieldInfo2;
							break;
						}
					}
					if (_skillLevelField == null)
					{
						yield break;
					}
				}
				float value2;
				try
				{
					value2 = (float)_skillLevelField.GetValue(value);
				}
				catch
				{
					continue;
				}
				yield return new KeyValuePair<string, float>(text, value2);
			}
		}

		private void SendSkillReportNow()
		{
			if (_serverRpc == null)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			Skills playerSkills = GetPlayerSkills(localPlayer);
			if ((Object)(object)playerSkills == (Object)null)
			{
				return;
			}
			StringBuilder stringBuilder = new StringBuilder();
			bool flag = true;
			foreach (KeyValuePair<string, float> item in EnumerateSkills(playerSkills))
			{
				string key = item.Key;
				float value = item.Value;
				if (!string.IsNullOrEmpty(key) && key.IndexOf(':') < 0 && key.IndexOf('|') < 0 && key.Length <= 32)
				{
					if (!flag)
					{
						stringBuilder.Append('|');
					}
					stringBuilder.Append(key);
					stringBuilder.Append(':');
					stringBuilder.Append(value.ToString("F1", CultureInfo.InvariantCulture));
					flag = false;
				}
			}
			if (stringBuilder.Length == 0)
			{
				return;
			}
			try
			{
				_serverRpc.Invoke("ServerGuard_SkillReport", new object[1] { stringBuilder.ToString() });
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] Skill report send failed: " + ex.Message));
				}
			}
		}

		private static bool IsActiveMultiplayerClient()
		{
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					return false;
				}
				if (ZNet.instance.IsServer())
				{
					return false;
				}
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal void ReportDevcommand(string command, string category = "cheat")
		{
			try
			{
				LogS.LogWarning((object)("[ServerGuard.Client] Blocked console command `" + command + "` (" + category + ") — server policy: " + _consoleMode));
				if (_serverRpc != null)
				{
					try
					{
						_serverRpc.Invoke("ServerGuard_DevcommandAttempt", new object[1] { command + "|" + category });
						return;
					}
					catch (Exception ex)
					{
						LogS.LogWarning((object)("[ServerGuard.Client] Could not report console attempt to server: " + ex.Message));
						return;
					}
				}
			}
			catch
			{
			}
		}

		private static void NotifyBlocked(string cmd, string category)
		{
			try
			{
				string text = ((category == "bind") ? "key binds are disabled on this server" : ((category == "notallowed") ? "this server only permits a whitelist of console commands" : "this command is blocked by the server's security policy"));
				Instance?.DisplayAdminReply("[ServerGuard] `" + cmd + "` refused — " + text + ".");
			}
			catch
			{
			}
		}

		internal void ReportAnimationCancel(string source)
		{
			try
			{
				LogS.LogInfo((object)("[ServerGuard.Client] Blocked animation cancel via " + source + " (mid-attack)."));
				if (_serverRpc != null)
				{
					try
					{
						_serverRpc.Invoke("ServerGuard_AnimationCancelAttempt", new object[1] { source ?? "" });
						return;
					}
					catch (Exception ex)
					{
						LogS.LogWarning((object)("[ServerGuard.Client] Could not report animation-cancel: " + ex.Message));
						return;
					}
				}
			}
			catch
			{
			}
		}

		private static IDictionary ResolveTerminalCommands()
		{
			if (_terminalCommandsResolved)
			{
				return _terminalCommands;
			}
			_terminalCommandsResolved = true;
			try
			{
				FieldInfo[] fields = typeof(Terminal).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (!typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType) || !fieldInfo.FieldType.IsGenericType)
					{
						continue;
					}
					Type[] genericArguments = fieldInfo.FieldType.GetGenericArguments();
					if (genericArguments.Length != 2 || genericArguments[0] != typeof(string) || genericArguments[1].Name.IndexOf("ConsoleCommand", StringComparison.OrdinalIgnoreCase) < 0)
					{
						continue;
					}
					_terminalCommands = fieldInfo.GetValue(null) as IDictionary;
					if (_terminalCommands != null)
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogInfo((object)$"[ServerGuard.Client] Terminal command registry bound: {fieldInfo.Name} ({_terminalCommands.Count} commands).");
						}
						return _terminalCommands;
					}
				}
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)"[ServerGuard.Client] Could not locate Terminal's command registry - falling back to the static command lists only.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS3 = LogS;
				if (logS3 != null)
				{
					logS3.LogWarning((object)("[ServerGuard.Client] Terminal command registry lookup failed: " + ex.Message));
				}
			}
			return _terminalCommands;
		}

		private static object LookupCommandObject(string cmd)
		{
			if (string.IsNullOrEmpty(cmd))
			{
				return null;
			}
			IDictionary dictionary = ResolveTerminalCommands();
			if (dictionary == null)
			{
				return null;
			}
			try
			{
				string key = cmd.ToLowerInvariant();
				if (dictionary.Contains(key))
				{
					return dictionary[key];
				}
				foreach (DictionaryEntry item in dictionary)
				{
					if (item.Key is string a && string.Equals(a, cmd, StringComparison.OrdinalIgnoreCase))
					{
						return item.Value;
					}
				}
			}
			catch
			{
			}
			return null;
		}

		private static bool IsRegisteredCheatCommand(string cmd)
		{
			object obj = LookupCommandObject(cmd);
			if (obj == null)
			{
				return false;
			}
			return ReadBoolMember(obj, new string[5] { "IsCheat", "isCheat", "m_isCheat", "Cheat", "cheat" });
		}

		private static bool IsRegisteredCommand(string cmd)
		{
			return LookupCommandObject(cmd) != null;
		}

		private static bool ReadBoolMember(object target, string[] names)
		{
			Type type = target.GetType();
			bool flag = default(bool);
			bool flag2 = default(bool);
			foreach (string name in names)
			{
				FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.FieldType == typeof(bool))
				{
					object value = field.GetValue(target);
					int num;
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
					return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
				}
				PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && property.PropertyType == typeof(bool))
				{
					object value = property.GetValue(target);
					int num2;
					if (value is bool)
					{
						flag2 = (bool)value;
						num2 = 1;
					}
					else
					{
						num2 = 0;
					}
					return (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0;
				}
			}
			return false;
		}

		internal static bool ShouldBlockConsoleCommand(string cmd, out string category)
		{
			category = null;
			if (string.IsNullOrEmpty(cmd))
			{
				return false;
			}
			if (ConsoleGuardExempt)
			{
				return false;
			}
			if (_consoleMode == "open")
			{
				return false;
			}
			if (_consoleBindPolicy != "allow" && BindCommands.Contains(cmd))
			{
				category = "bind";
				return true;
			}
			if (_consoleExtraBlocked.Contains(cmd))
			{
				category = "risky";
				return true;
			}
			if (_consoleMode == "whitelist")
			{
				if (AlwaysAllowedCommands.Contains(cmd))
				{
					return false;
				}
				if (_consoleAllowed.Contains(cmd))
				{
					return false;
				}
				if (!IsRegisteredCommand(cmd))
				{
					return false;
				}
				category = "notallowed";
				return true;
			}
			if (CheatCommands.Contains(cmd))
			{
				category = "cheat";
				return true;
			}
			if (IsRegisteredCheatCommand(cmd))
			{
				category = "cheat";
				return true;
			}
			if (RiskyCommands.Contains(cmd))
			{
				category = "risky";
				return true;
			}
			return false;
		}

		private static bool ShouldBlockAnimationCancel(Player p)
		{
			try
			{
				if (!IsActiveMultiplayerClient())
				{
					return false;
				}
				if (IsOwnerClient)
				{
					return false;
				}
				if ((Object)(object)p == (Object)null)
				{
					return false;
				}
				if ((Object)(object)p != (Object)(object)Player.m_localPlayer)
				{
					return false;
				}
				return ((Character)p).InAttack();
			}
			catch
			{
				return false;
			}
		}

		private static void ResolveBindFields()
		{
			if (_bindFieldsResolved)
			{
				return;
			}
			_bindFieldsResolved = true;
			try
			{
				FieldInfo[] fields = typeof(Terminal).GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType.IsGenericType)
					{
						Type[] genericArguments = fieldInfo.FieldType.GetGenericArguments();
						if (_bindsField == null && genericArguments.Length == 2 && genericArguments[0] == typeof(KeyCode) && typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType))
						{
							_bindsField = fieldInfo;
						}
						else if (_bindListField == null && genericArguments.Length == 1 && genericArguments[0] == typeof(string) && typeof(IList).IsAssignableFrom(fieldInfo.FieldType))
						{
							_bindListField = fieldInfo;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] Bind field lookup failed: " + ex.Message));
				}
			}
			if (_bindsField == null)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)"[ServerGuard.Client] Could not locate Terminal's bind table - bind purge is inactive.");
				}
			}
		}

		internal static void ApplyBindPolicy()
		{
			try
			{
				if (ConsoleGuardExempt || _consoleBindPolicy == "allow" || _consoleBindPolicy == "block" || !IsActiveMultiplayerClient())
				{
					return;
				}
				ResolveBindFields();
				int num = 0;
				if (_bindsField?.GetValue(null) is IDictionary { Count: >0 } dictionary)
				{
					num = dictionary.Count;
					dictionary.Clear();
				}
				if (_consoleBindPolicy == "wipe" && _bindListField?.GetValue(null) is IList { Count: >0 } list)
				{
					list.Clear();
					try
					{
						typeof(Terminal).GetMethod("updateBinds", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(null, null);
					}
					catch (Exception ex)
					{
						ManualLogSource logS = LogS;
						if (logS != null)
						{
							logS.LogWarning((object)("[ServerGuard.Client] Could not persist bind wipe: " + ex.Message));
						}
					}
				}
				if (num > 0)
				{
					ManualLogSource logS2 = LogS;
					if (logS2 != null)
					{
						logS2.LogWarning((object)$"[ServerGuard.Client] Cleared {num} key bind(s) — server bind policy is '{_consoleBindPolicy}'.");
					}
					Instance?.DisplayAdminReply($"[ServerGuard] {num} custom key bind(s) removed — this server does not permit console key binds.");
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logS3 = LogS;
				if (logS3 != null)
				{
					logS3.LogWarning((object)("[ServerGuard.Client] ApplyBindPolicy error: " + ex2.Message));
				}
			}
		}

		internal static void OnArrivalShoutPolicyReceived(string payload)
		{
			_arrivalShoutAllowed = !string.Equals((payload ?? "").Trim(), "0", StringComparison.Ordinal);
			_arrivalShoutConsumed = false;
			_inRespawnUpdate = false;
			ManualLogSource logS = LogS;
			if (logS != null)
			{
				logS.LogInfo((object)("[ServerGuard.Client] Arrival shout " + (_arrivalShoutAllowed ? "allowed" : "suppressed") + " by server policy."));
			}
		}

		internal void SendChatReport(int type, string text)
		{
			try
			{
				ZNet instance = ZNet.instance;
				ZRpc val = ((instance != null) ? instance.GetServerRPC() : null);
				if (val == null)
				{
					return;
				}
				text = text.Replace('\n', ' ').Trim();
				if (text.Length > 256)
				{
					text = text.Substring(0, 256);
				}
				if (text.Length != 0)
				{
					val.Invoke("ServerGuard_Chat", new object[1] { $"{type}|{text}" });
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogInfo((object)"[ServerGuard.Client] Shout report sent.");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] Chat report failed: " + ex.Message));
				}
			}
		}

		internal void OnRemoveItemsReceived(string itemList)
		{
			try
			{
				if (!string.IsNullOrWhiteSpace(itemList))
				{
					string[] array = (from s in itemList.Split(new char[1] { ',' })
						select s.Trim() into s
						where !string.IsNullOrEmpty(s)
						select s).ToArray();
					if (array.Length != 0)
					{
						((MonoBehaviour)this).StartCoroutine(RemoveItemsFromInventory(array));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] OnRemoveItemsReceived error: " + ex.Message));
				}
			}
		}

		private IEnumerator RemoveItemsFromInventory(string[] prefabNames)
		{
			for (float elapsed = 0f; elapsed < 90f; elapsed += 0.5f)
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null && ((Humanoid)Player.m_localPlayer).GetInventory() != null)
				{
					break;
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				yield break;
			}
			try
			{
				Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
				if (inventory == null)
				{
					yield break;
				}
				List<ItemData> list = new List<ItemData>();
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && prefabNames.Contains<string>(((Object)allItem.m_dropPrefab).name, StringComparer.OrdinalIgnoreCase))
					{
						list.Add(allItem);
					}
				}
				foreach (ItemData item in list)
				{
					inventory.RemoveItem(item);
				}
				if (list.Count <= 0)
				{
					yield break;
				}
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)string.Format("[ServerGuard.Client] Removed {0} cheat item(s) from inventory: {1}", list.Count, string.Join(", ", list.Select((ItemData i) => ((Object)i.m_dropPrefab).name))));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS2 = LogS;
				if (logS2 != null)
				{
					logS2.LogWarning((object)("[ServerGuard.Client] RemoveItemsFromInventory error: " + ex.Message));
				}
			}
		}

		internal void BuildQuickLoginPanel(FejdStartup menu)
		{
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_0148: 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_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: 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_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			if (_clientSettings == null || !_clientSettings.QuickLoginEnabled || string.IsNullOrWhiteSpace(_clientSettings.ServerAddress))
			{
				return;
			}
			if ((Object)(object)_quickLoginPanel != (Object)null)
			{
				Object.Destroy((Object)(object)_quickLoginPanel);
				_quickLoginPanel = null;
				_playerCountText = null;
			}
			Transform val = null;
			object field = GetField(menu, "m_characterSelectScreen");
			GameObject val2 = (GameObject)((field is GameObject) ? field : null);
			if ((Object)(object)val2 != (Object)null && (Object)(object)val2.transform.parent != (Object)null)
			{
				val = val2.transform.parent;
			}
			if ((Object)(object)val == (Object)null)
			{
				Canvas componentInChildren = ((Component)menu).GetComponentInChildren<Canvas>(true);
				val = (((Object)(object)componentInChildren != (Object)null) ? ((Component)componentInChildren).transform : null);
			}
			if ((Object)(object)val == (Object)null)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)"[ServerGuard.Client] BuildQuickLoginPanel: no GUI root found.");
				}
				return;
			}
			_quickLoginPanel = new GameObject("SG_QuickLogin");
			_quickLoginPanel.transform.SetParent(val, false);
			RectTransform val3 = _quickLoginPanel.AddComponent<RectTransform>();
			val3.anchorMin = new Vector2(1f, 1f);
			val3.anchorMax = new Vector2(1f, 1f);
			val3.pivot = new Vector2(1f, 1f);
			val3.sizeDelta = new Vector2(320f, 440f);
			val3.anchoredPosition = new Vector2(-30f, -70f);
			((Graphic)_quickLoginPanel.AddComponent<Image>()).color = new Color(0.05f, 0.05f, 0.05f, 0.82f);
			float num = -10f;
			if (!string.IsNullOrWhiteSpace(_clientSettings.ServerLogoPath))
			{
				Texture2D val4 = LoadTexture(Path.Combine(ConfDir, _clientSettings.ServerLogoPath));
				if ((Object)(object)val4 != (Object)null)
				{
					GameObject obj = CreateChild("SG_Logo", _quickLoginPanel.transform);
					RectTransform obj2 = obj.AddComponent<RectTransform>();
					obj2.anchorMin = new Vector2(0.05f, 1f);
					obj2.anchorMax = new Vector2(0.95f, 1f);
					obj2.pivot = new Vector2(0.5f, 1f);
					obj2.anchoredPosition = new Vector2(0f, num);
					float num2 = (float)((Texture)val4).width / (float)((Texture)val4).height;
					float num3 = Mathf.Min(120f, 300f / num2);
					obj2.sizeDelta = new Vector2(0f, num3);
					Image obj3 = obj.AddComponent<Image>();
					obj3.sprite = Sprite.Create(val4, new Rect(0f, 0f, (float)((Texture)val4).width, (float)((Texture)val4).height), new Vector2(0.5f, 0.5f));
					obj3.preserveAspect = true;
					num -= num3 + 8f;
				}
			}
			Component tmpTemplate = (Component)(((object)GetMenuButtonLabelTemplate(menu)) ?? ((object)/*isinst with value type is only supported in some contexts*/));
			num = AddThemedLabel("SG_Name", _quickLoginPanel.transform, tmpTemplate, _clientSettings.ServerName, 24f, bold: false, Color.white, num, 32f);
			if (!string.IsNullOrWhiteSpace(_clientSettings.ServerDescription))
			{
				num = AddThemedLabel("SG_Desc", _quickLoginPanel.transform, tmpTemplate, _clientSettings.ServerDescription, 16f, bold: false, new Color(0.85f, 0.85f, 0.85f, 1f), num, 64f);
			}
			bool flag = !string.IsNullOrWhiteSpace(_clientSettings.ServerAnnouncements);
			if (flag)
			{
				float num4 = 0f - num + 26f + 140f + 100f;
				val3.sizeDelta = new Vector2(320f, Mathf.Clamp(num4, 560f, 720f));
				num = AddThemedLabel("SG_AnnHeader", _quickLoginPanel.transform, tmpTemplate, "Announcements", 18f, bold: true, new Color(0.95f, 0.85f, 0.55f, 1f), num, 22f);
				BuildAnnouncementsScrollBox(_quickLoginPanel.transform, tmpTemplate, _clientSettings.ServerAnnouncements, num, 100f);
			}
			_playerCountText = (flag ? CreateThemedLabelComponent("SG_PlayerCount", _quickLoginPanel.transform, tmpTemplate, "Players: querying...", 17f, bold: false, new Color(0.7f, 0.9f, 0.7f, 1f), 0f, 24f, anchorBottom: true, 72f) : CreateThemedLabelComponent("SG_PlayerCount", _quickLoginPanel.transform, tmpTemplate, "Players: querying...", 17f, bold: false, new Color(0.7f, 0.9f, 0.7f, 1f), num - 6f, 26f));
			AddConnectButton(menu, _quickLoginPanel.transform);
			_quickLoginPanel.transform.SetAsLastSibling();
			((MonoBehaviour)this).StartCoroutine(RefreshPlayerCount(_clientSettings.ServerAddress, _clientSettings.ServerPort));
		}

		private void BuildAnnouncementsScrollBox(Transform parent, Component tmpTemplate, string raw, float topOffset, float bottomInset)
		{
			//IL_0013: 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_0033: 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_0056: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: 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_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: 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_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateChild("SG_AnnScroll", parent);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.pivot = new Vector2(0.5f, 0.5f);
			obj.offsetMin = new Vector2(16f, bottomInset);
			obj.offsetMax = new Vector2(-16f, topOffset);
			((Graphic)val.AddComponent<Image>()).color = new Color(0f, 0f, 0f, 0.35f);
			ScrollRect val2 = val.AddComponent<ScrollRect>();
			val2.horizontal = false;
			val2.vertical = true;
			val2.movementType = (MovementType)2;
			val2.scrollSensitivity = 24f;
			val2.inertia = false;
			GameObject val3 = CreateChild("SG_AnnViewport", val.transform);
			RectTransform val4 = val3.AddComponent<RectTransform>();
			val4.anchorMin = Vector2.zero;
			val4.anchorMax = Vector2.one;
			val4.pivot = new Vector2(0f, 1f);
			val4.offsetMin = Vector2.zero;
			val4.offsetMax = new Vector2(-8f, 0f);
			Image obj2 = val3.AddComponent<Image>();
			((Graphic)obj2).color = new Color(1f, 1f, 1f, 0f);
			((Graphic)obj2).raycastTarget = true;
			val3.AddComponent<RectMask2D>();
			GameObject val5 = CreateChild("SG_AnnContent", val3.transform);
			RectTransform val6 = val5.AddComponent<RectTransform>();
			val6.anchorMin = new Vector2(0f, 1f);
			val6.anchorMax = new Vector2(1f, 1f);
			val6.pivot = new Vector2(0.5f, 1f);
			val6.anchoredPosition = Vector2.zero;
			val6.sizeDelta = new Vector2(0f, 140f);
			Component textComp = CreateAnnouncementText(val5.transform, tmpTemplate, raw);
			GameObject val7 = CreateChild("SG_AnnScrollbar", val.transform);
			RectTransform obj3 = val7.AddComponent<RectTransform>();
			obj3.anchorMin = new Vector2(1f, 0f);
			obj3.anchorMax = new Vector2(1f, 1f);
			obj3.pivot = new Vector2(1f, 1f);
			obj3.sizeDelta = new Vector2(8f, 0f);
			obj3.anchoredPosition = Vector2.zero;
			((Graphic)val7.AddComponent<Image>()).color = new Color(1f, 1f, 1f, 0.07f);
			Scrollbar val8 = val7.AddComponent<Scrollbar>();
			val8.direction = (Direction)2;
			GameObject val9 = CreateChild("SG_AnnScrollArea", val7.transform);
			RectTransform obj4 = val9.AddComponent<RectTransform>();
			obj4.anchorMin = Vector2.zero;
			obj4.anchorMax = Vector2.one;
			obj4.offsetMin = Vector2.zero;
			obj4.offsetMax = Vector2.zero;
			GameObject obj5 = CreateChild("SG_AnnScrollHandle", val9.transform);
			RectTransform val10 = obj5.AddComponent<RectTransform>();
			val10.offsetMin = Vector2.zero;
			val10.offsetMax = Vector2.zero;
			Image val11 = obj5.AddComponent<Image>();
			((Graphic)val11).color = new Color(0.85f, 0.78f, 0.6f, 0.55f);
			((Selectable)val8).targetGraphic = (Graphic)(object)val11;
			val8.handleRect = val10;
			val2.viewport = val4;
			val2.content = val6;
			val2.verticalScrollbar = val8;
			val2.verticalScrollbarVisibility = (ScrollbarVisibility)0;
			((MonoBehaviour)this).StartCoroutine(FitAnnouncementContent(val2, val4, val6, textComp));
		}

		private Component CreateAnnouncementText(Transform content, Component tmpTemplate, string raw)
		{
			//IL_0081: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: 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)
			GameObject val = (((Object)(object)tmpTemplate != (Object)null) ? Object.Instantiate<GameObject>(tmpTemplate.gameObject, content, false) : CreateChild("SG_AnnText", content));
			((Object)val).name = "SG_AnnText";
			val.SetActive(true);
			ContentSizeFitter component = val.GetComponent<ContentSizeFitter>();
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = false;
				Object.Destroy((Object)(object)component);
			}
			LayoutElement component2 = val.GetComponent<LayoutElement>();
			if ((Object)(object)component2 != (Object)null)
			{
				((Behaviour)component2).enabled = false;
				Object.Destroy((Object)(object)component2);
			}
			RectTransform obj = val.GetComponent<RectTransform>() ?? val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.pivot = new Vector2(0.5f, 1f);
			obj.offsetMin = new Vector2(6f, 6f);
			obj.offsetMax = new Vector2(-6f, -6f);
			((Transform)obj).localScale = Vector3.one;
			if ((Object)(object)tmpTemplate != (Object)null)
			{
				Component component3 = val.GetComponent(((object)tmpTemplate).GetType());
				SetTmpProperty(component3, "text", FormatAnnouncementsRich(raw));
				SetTmpProperty(component3, "fontSize", 15f);
				SetTmpProperty(component3, "color", (object)new Color(0.88f, 0.88f, 0.88f, 1f));
				SetTmpProperty(component3, "enableAutoSizing", false);
				SetTmpProperty(component3, "enableWordWrapping", true);
				SetTmpProperty(component3, "richText", true);
				SetTmpProperty(component3, "raycastTarget", true);
				SetTmpEnum(component3, "overflowMode", "Overflow");
				SetTmpEnum(component3, "alignment", "TopLeft");
				SetTmpEnum(component3, "fontStyle", "Normal");
				val.AddComponent<AnnouncementLinkClicker>().Init(component3);
				return component3;
			}
			Text obj2 = val.AddComponent<Text>();
			obj2.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj2.fontSize = 15;
			((Graphic)obj2).color = new Color(0.88f, 0.88f, 0.88f, 1f);
			obj2.alignment = (TextAnchor)0;
			obj2.supportRichText = true;
			obj2.horizontalOverflow = (HorizontalWrapMode)0;
			obj2.verticalOverflow = (VerticalWrapMode)1;
			obj2.text = FormatAnnouncementsPlain(raw);
			return (Component)(object)obj2;
		}

		private IEnumerator FitAnnouncementContent(ScrollRect scroll, RectTransform viewport, RectTransform content, Component textComp)
		{
			for (int pass = 0; pass < 2; pass++)
			{
				yield return null;
				if ((Object)(object)scroll == (Object)null || (Object)(object)content == (Object)null || (Object)(object)textComp == (Object)null)
				{
					yield break;
				}
				Canvas.ForceUpdateCanvases();
				float num = 0f;
				Text val = (Text)(object)((textComp is Text) ? textComp : null);
				Rect rect;
				if ((Object)(object)val != (Object)null)
				{
					num = val.preferredHeight;
				}
				else
				{
					object tmpProperty = GetTmpProperty(textComp, "preferredHeight");
					if (tmpProperty is float)
					{
						num = (float)tmpProperty;
					}
					if (num <= 0f)
					{
						rect = content.rect;
						num = MeasureTmpHeight(textComp, ((Rect)(ref rect)).width - 12f);
					}
				}
				float num2 = num + 12f;
				rect = viewport.rect;
				content.sizeDelta = new Vector2(0f, Mathf.Max(num2, ((Rect)(ref rect)).height));
			}
			scroll.verticalNormalizedPosition = 1f;
		}

		private static float MeasureTmpHeight(Component tmp, float width)
		{
			if ((Object)(object)tmp == (Object)null || width <= 0f)
			{
				return 0f;
			}
			try
			{
				MethodInfo[] methods = ((object)tmp).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public);
				foreach (MethodInfo methodInfo in methods)
				{
					if (methodInfo.Name != "GetPreferredValues")
					{
						continue;
					}
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 2 && !(parameters[0].ParameterType != typeof(float)) && !(parameters[1].ParameterType != typeof(float)))
					{
						object obj = methodInfo.Invoke(tmp, new object[2] { width, 32767f });
						if (obj is Vector2)
						{
							return ((Vector2)obj).y;
						}
						return 0f;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logS = LogS;
				if (logS != null)
				{
					logS.LogWarning((object)("[ServerGuard.Client] Announcement height measurement failed: " + ex.Message));
				}
			}
			return 0f;
		}

		internal static string FormatAnnouncementsRich(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return "";
			}
			return AnnLinkRegex.Replace(raw.Replace("\r\n", "\n").TrimEnd(Array.Empty<char>()), delegate(Match m)
			{
				string value = m.Groups[1].Value;
				string value2 = m.Groups[2].Value;
				return (!IsOpenableUrl(value2)) ? value : ("<link=\"" + value2 + "\"><color=#7FB3FF><u>" + value + "</u></color></link>");
			});
		}

		internal static string FormatAnnouncementsPlain(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return "";
			}
			return AnnLinkRegex.Replace(raw.Replace("\r\n", "\n").TrimEnd(Array.Empty<char>()), (Match m) => m.Groups[1].Value + " (" + m.Groups[2].Value + ")");
		}

		private static bool IsOpenableUrl(string url)
		{
			if (string.IsNullOrWhiteSpace(url))
			{
				return false;
			}
			if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
			{
				return url.StartsWith("https://", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static GameObject CreateChild(string name, Transform parent)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			return val;
		}

		private static object GetField(object obj, string name)
		{
			return (obj?.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(obj);
		}

		private static Component GetMenuButtonLabelTemplate(FejdStartup menu)
		{
			Button val = (GetField(menu, "m_menuButtons") as Button[])?.FirstOrDefault((Func<Button, bool>)((Button b) => (Object)(object)b != (Object)null));
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			Component[] componentsInChildren = ((Component)val).GetComponentsInChildren<Component>(true);
			foreach (Component val2 in componentsInChildren)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					string name = ((object)val2).GetType().Name;
					if (name == "TextMeshProUGUI" || name == "TMP_Text")
					{
						return val2;
					}
				}
			}
			return null;
		}

		private float AddThemedLabel(string name, Transform parent, Component tmpTemplate, string text, float fontSize, bool bold, Color color, float topOffset, float height)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			CreateThemedLabelComponent(name, parent, tmpTemplate, text, fontSize, bold, color, topOffset, height);
			return topOffset - (height + 4f);
		}

		private Component CreateThemedLabelComponent(string name, Transform parent, Component tmpTemplate, string text, float fontSize, bool bold, Color color, float topOffset, float height, bool anchorBottom = false, float bottomOffset = 0f)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: 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_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)tmpTemplate != (Object)null) ? Object.Instantiate<GameObject>(tmpTemplate.gameObject, parent, false) : CreateChild(name, parent));
			((Object)val).name = name;
			val.SetActive(true);
			ContentSizeFitter component = val.GetComponent<ContentSizeFitter>();
			if ((Object)(object)component != (Object)null)
			{
				Object.Destroy((Object)(object)component);
			}
			LayoutElement component2 = val.GetComponent<LayoutElement>();
			if ((Object)(object)component2 != (Object)null)
			{
				Object.Destroy((Object)(object)component2);
			}
			RectTransform val2 = val.GetComponent<RectTransform>() ?? val.AddComponent<RectTransform>();
			if (anchorBottom)
			{
				val2.anchorMin = new Vector2(0.05f, 0f);
				val2.anchorMax = new Vector2(0.95f, 0f);
				val2.pivot = new Vector2(0.5f, 0f);
				val2.anchoredPosition = new Vector2(0f, bottomOffset);
			}
			else
			{
				val2.anchorMin = new Vector2(0.05f, 1f);
				val2.anchorMax = new Vector2(0.95f, 1f);
				val2.pivot = new Vector2(0.5f, 1f);
				val2.anchoredPosition = new Vector2(0f, topOffset);
			}
			val2.sizeDelta = new Vector2(0f, height);
			((Transform)val2).localScale = Vector3.one;
			if ((Object)(object)tmpTemplate != (Object)null)
			{
				Component component3 = val.GetComponent(((object)tmpTemplate).GetType());
				SetTmpProperty(component3, "text", text);
				SetTmpProperty(component3, "fontSize", fontSize);
				SetTmpProperty(component3, "color", color);
				SetTmpProperty(component3, "enableAutoSizing", false);
				SetTmpProperty(component3, "enableWordWrapping", true);
				SetTmpEnum(component3, "overflowMode", "Overflow");
				SetTmpEnum(component3, "alignment", "Top");
				SetTmpEnum(component3, "fontStyle", bold ? "Bold" : "Normal");
				return component3;
			}
			Text obj = val.AddComponent<Text>();
			obj.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj.fontSize = Mathf.RoundToInt(fontSize);
			obj.fontStyle = (FontStyle)(bold ? 1 : 0);
			((Graphic)obj).color = color;
			obj.alignment = (TextAnchor)1;
			obj.horizontalOverflow = (HorizontalWrapMode)0;
			obj.verticalOverflow = (VerticalWrapMode)1;
			obj.text = text;
			return (Component)(object)obj;
		}

		private void AddConnectButton(FejdStartup menu, Transform parent)
		{
			//IL_0148: 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_0172: 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_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: 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_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: 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_00d0: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Expected O, but got Unknown
			Button val = (GetField(menu, "m_menuButtons") as Button[])?.FirstOrDefault((Func<Button, bool>)((Button b) => (Object)(object)b != (Object)null));
			if ((Object)(object)val != (Object)null)
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, parent, false);
				((Object)obj).name = "SG_ConnectBtn";
				obj.SetActive(true);
				RectTransform component = obj.GetComponent<RectTransform>();
				component.anchorMin = new Vector2(0.5f, 0f);
				component.anchorMax = new Vector2(0.5f, 0f);
				component.pivot = new Vector2(0.5f, 0f);
				component.anchoredPosition = new Vector2(0f, 16f);
				component.sizeDelta = new Vector2(240f, 48f);
				((Transform)component).localScale = Vector3.one;
				SetAnyText(obj, "Connect");
				Button component2 = obj.GetComponent<Button>();
				component2.onClick = new ButtonClickedEvent();
				((UnityEvent)component2.onClick).AddListener((UnityAction)delegate
				{
					ConnectToConfiguredServer(menu);
				});
				return;
			}
			GameObject val2 = CreateChild("SG_ConnectBtn", parent);
			RectTransform obj2 = val2.AddComponent<RectTransform>();
			obj2.anchorMin = new Vector2(0.5f, 0f);
			obj2.anchorMax = new Vector2(0.5f, 0f);
			obj2.pivot = new Vector2(0.5f, 0f);
			obj2.anchoredPosition = new Vector2(0f, 16f);
			obj2.sizeDelta = new Vector2(240f, 44f);
			((Graphic)val2.AddComponent<Image>()).color = new Color(0.15f, 0.45f, 0.15f, 1f);
			Button obj3 = val2.AddComponent<Button>();
			GameObject obj4 = CreateChild("SG_ConnectBtnText", val2.transform);
			RectTransform obj5 = obj4.AddComponent<RectTransform>();
			obj5.anchorMin = Vector2.zero;
			obj5.anchorMax = Vector2.one;
			obj5.offsetMin = Vector2.zero;
			obj5.offsetMax = Vector2.zero;
			Text obj6 = obj4.AddComponent<Text>();
			obj6.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			obj6.fontSize = 18;
			obj6.fontStyle = (FontStyle)1;
			((Graphic)obj6).color = Color.white;
			obj6.alignment = (TextAnchor)4;
			obj6.text = "Connect";
			((UnityEvent)obj3.onClick).AddListener((UnityAction)delegate
			{
				ConnectToConfiguredServer(menu);
			});
		}

		private static void SetAnyText(GameObject go, string text)
		{
			Component[] componentsInChildren = go.GetComponentsInChildren<Component>(true);
			foreach (Component val in componentsInChildren)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				string name = ((object)val).GetType().Name;
				if (name == "TextMeshProUGUI" || name == "TMP_Text")
				{
					SetTmpProperty(val, "text", text);
					continue;
				}
				Text val2 = (Text)(object)((val is Text) ? val : null);
				if (val2 != null)
				{
					val2.text = text;
				}
			}
		}

		private static void SetAnyText(Component comp, string text)
		{
			if (!((Object)(object)comp == (Object)null))
			{
				Text val = (Text)(object)((comp is Text) ? comp : null);
				if (val != null)
				{
					val.text = text;
				}
				else
				{
					SetTmpProperty(comp, "text", text);
				}
			}
		}

		private static void SetTmpProperty(object tmp, string prop, object val)
		{
			if (tmp == null)
			{
				return;
			}
			PropertyInfo property = tmp.GetType().GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property != null) || !property.CanWrite)
			{
				return;
			}
			try
			{
				property.SetValue(tmp, val, null);
			}
			catch
			{
			}
		}

		private static object GetTmpProperty(object tmp, string prop)
		{
			if (tmp == null)
			{
				return null;
			}
			PropertyInfo property = tmp.GetType().GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null || !property.CanRead)
			{
				return null;
			}
			try
			{
				return property.GetValue(tmp, null);
			}
			catch
			{
				return null;
			}
		}

		private static void SetTmpEnum(object tmp, string prop, string enumName)
		{
			if (tmp == null)
			{
				return;
			}
			PropertyInfo property = tmp.GetType().GetProperty(prop, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property == null)
			{
				return;
			}
			try
			{
				object value = Enum.Parse(property.PropertyType, enumName);
				property.SetValue(tmp, value, null);
			}
			catch
			{
			}
		}

		private static Texture2D LoadTexture(string path)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			try
			{
				if (!File.Exists(path))
				{
					ManualLogSource logS = LogS;
					if (logS != null)
					{
						logS.LogWarning((object)("[ServerGuard.Client] Logo file not found: " + path));
					}
					return null;
				}
				byte[] array = File.ReadAllBytes(path);
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
				MethodInfo methodInfo = AppDomain.CurrentDomain.GetAssemblies().Select(delegate(Assembly a)
				{
					try
					{
						return a.GetType("UnityEngine.ImageConversion");
					}
					catch
					{
						return (Type)null;
					}
				}).FirstOrDefault((Type t) => t != null)?.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(Texture2D),
					typeof(byte[])
				}, null);
				if (methodInfo != null)
				{
					object obj = methodInfo.Invoke(null, new object[2] { val, array });
					if (obj is bool && !(bool)obj)
					{
						ManualLogSource logS2 = LogS;
						if (logS2 != null)
						{
							logS2.LogWarning((object)"[ServerGuard.Client] ImageConversion.LoadImage returned false — unsupported image (use PNG or JPG).");
						}
					}
					return val;
				}
				MethodInfo method = typeof(Texture2D).GetMethod("LoadImage