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 BepInEx; using BepInEx.Bootstrap; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; 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.6.0.0")] [assembly: AssemblyInformationalVersion("1.6.0+1f31bf8116fc4846821d0c6e643692b47cd65e03")] [assembly: AssemblyProduct("Valheim-ServerGuard-Client")] [assembly: AssemblyTitle("Valheim-ServerGuard-Client")] [assembly: AssemblyVersion("1.6.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimServerGuard.Shared { public static class ModsetFingerprint { public static string ComputeStrict(IEnumerable> entries) { return Sha256Hex(Canonicalize(entries, withHash: true)); } public static string ComputeLoose(IEnumerable> 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> entries, bool withHash) { if (entries == null) { return ""; } List list = (from e in entries where !string.IsNullOrWhiteSpace(e.Key) select (!withHash) ? (e.Key ?? "").ToLowerInvariant() : ((e.Key ?? "").ToLowerInvariant() + "|" + (e.Value ?? "").ToLowerInvariant())).Distinct(StringComparer.Ordinal).OrderBy((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 Mods = new List(); public string Hmac; public string CanonicalForHmac() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(SchemaVersion ?? "").Append('|'); stringBuilder.Append(Challenge ?? "").Append('|'); stringBuilder.Append(TimestampUtc).Append('|'); List list = new List(Mods ?? new List()); 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.6.0")] 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; } = ""; } [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("ServerGuard_RequestManifest", (Action)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("ServerGuard_AdminCommandReply", (Action)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("ServerGuard_RemoveItems", (Action)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)); } } }); 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(Humanoid), "HideHandItems")] public static class Patch_Humanoid_HideHandItems_BlockDuringAttack { public static bool Prefix(Humanoid __instance) { try { Player val = (Player)(object)((__instance is Player) ? __instance : null); if ((Object)(object)val == (Object)null) { return true; } if (ShouldBlockAnimationCancel(val)) { Instance?.ReportAnimationCancel("sheathe"); return false; } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Sheathe gate error: " + ex.Message)); } } return true; } } [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()); 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()) : ""); Instance?.SendAdminCommand(command); return false; } if (!IsActiveMultiplayerClient()) { return true; } bool num2 = BlockedCommands.Contains(text4); bool flag = !num2 && IsRegisteredCheatCommand(text4); if (num2 || flag) { Instance?.ReportDevcommand(text4); return false; } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Devcommand gate error: " + ex.Message)); } } return true; } } [HarmonyPatch(typeof(Chat), "SendText")] public static class Patch_Chat_SendText_Report { public static void Prefix(Type __0, string __1) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected I4, but got Unknown try { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && (int)__0 == 2 && !string.IsNullOrWhiteSpace(__1)) { Instance?.SendChatReport((int)__0, __1); } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Chat hook error: " + ex.Message)); } } } } [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(); } } public const string GUID = "com.taeguk.valheim.serverguard.client"; public const string NAME = "Valheim ServerGuard Client"; public const string VERSION = "1.6.0"; internal static ClientPlugin Instance; internal static ManualLogSource LogS; private Harmony _harmony; private string _sharedSecret = ""; private List _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 BlockedCommands = new HashSet(StringComparer.OrdinalIgnoreCase) { "devcommands", "debugmode", "imacheater", "god", "ghost", "fly", "nocost", "noplacementcost", "spawn", "pos", "goto", "tame", "killall", "event", "stopevent", "tod", "skiptime", "sleep", "raiseskill", "resetcharacter", "heal", "puke", "damage", "setkey", "resetkeys", "removedrops", "freefly" }; 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 _clientLastHitOnPiece = new ConditionalWeakTable(); 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 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> entries = (_cachedManifest ?? new List()).Select((ModManifestEntry m) => new KeyValuePair((!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.6.0", _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 list = _cachedManifest ?? new List(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# ServerGuard - allowed_mods snippet generated by ServerGuard.Client v1.6.0"); 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 /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-keyed, hash-pinned)."); stringBuilder.AppendLine("# To loosen, drop the `|` 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.6.0"); } stringBuilder.AppendLine(); stringBuilder.AppendLine("allowed_mods:"); List list2 = list.Where((ModManifestEntry m) => !string.Equals(m.Guid, "com.taeguk.valheim.serverguard.client", StringComparison.OrdinalIgnoreCase)).OrderBy((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_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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/"); File.WriteAllText(ClientYaml, stringBuilder.ToString()); } ClientSettings clientSettings = ((BuilderSkeleton)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).IgnoreUnmatchedProperties().Build() .Deserialize(File.ReadAllText(ClientYaml)) ?? new ClientSettings(); _sharedSecret = clientSettings.SharedSecret ?? ""; _clientSettings = clientSettings; } catch (Exception ex) { LogS.LogWarning((object)("[ServerGuard.Client] EnsureConfig failed: " + ex.Message)); } } private void BuildManifestCache() { _cachedManifest = new List(); try { foreach (KeyValuePair 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()) }; 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(); } 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> 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(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 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) { try { LogS.LogWarning((object)("[ServerGuard.Client] Blocked cheat attempt: `" + command + "` (multiplayer client)")); if (_serverRpc != null) { try { _serverRpc.Invoke("ServerGuard_DevcommandAttempt", new object[1] { command ?? "" }); return; } catch (Exception ex) { LogS.LogWarning((object)("[ServerGuard.Client] Could not report devcommand to server: " + ex.Message)); return; } } } 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 bool IsRegisteredCheatCommand(string cmd) { if (string.IsNullOrEmpty(cmd)) { return false; } try { Type? typeFromHandle = typeof(Terminal); IDictionary dictionary = null; FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (typeof(IDictionary).IsAssignableFrom(fieldInfo.FieldType) && fieldInfo.GetValue(null) is IDictionary dictionary2) { dictionary = dictionary2; break; } } if (dictionary == null) { return false; } object obj = null; foreach (DictionaryEntry item in dictionary) { if (item.Key is string a && string.Equals(a, cmd, StringComparison.OrdinalIgnoreCase)) { obj = item.Value; break; } } if (obj == null) { return false; } Type type = obj.GetType(); string[] array = new string[5] { "IsCheat", "isCheat", "m_isCheat", "Cheat", "cheat" }; bool flag = default(bool); bool flag2 = default(bool); foreach (string name in array) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null && field.FieldType == typeof(bool)) { object value = field.GetValue(obj); 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(obj); int num2; if (value is bool) { flag2 = (bool)value; num2 = 1; } else { num2 = 0; } return (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0; } } } catch { } return false; } private static bool ShouldBlockAnimationCancel(Player p) { try { if (!IsActiveMultiplayerClient()) { 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; } } 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 list = new List(); foreach (ItemData allItem in inventory.GetAllItems()) { if (!((Object)(object)allItem?.m_dropPrefab == (Object)null) && prefabNames.Contains(((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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_015b: 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_0304: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0233: Unknown result type (might be due to invalid IL or missing references) //IL_026b: Unknown result type (might be due to invalid IL or missing references) //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_035e: 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(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 obj = _quickLoginPanel.AddComponent(); obj.anchorMin = new Vector2(1f, 1f); obj.anchorMax = new Vector2(1f, 1f); obj.pivot = new Vector2(1f, 1f); obj.sizeDelta = new Vector2(320f, 440f); obj.anchoredPosition = new Vector2(-30f, -70f); ((Graphic)_quickLoginPanel.AddComponent()).color = new Color(0.05f, 0.05f, 0.05f, 0.82f); float num = -10f; if (!string.IsNullOrWhiteSpace(_clientSettings.ServerLogoPath)) { Texture2D val3 = LoadTexture(Path.Combine(ConfDir, _clientSettings.ServerLogoPath)); if ((Object)(object)val3 != (Object)null) { GameObject obj2 = CreateChild("SG_Logo", _quickLoginPanel.transform); RectTransform obj3 = obj2.AddComponent(); obj3.anchorMin = new Vector2(0.05f, 1f); obj3.anchorMax = new Vector2(0.95f, 1f); obj3.pivot = new Vector2(0.5f, 1f); obj3.anchoredPosition = new Vector2(0f, num); float num2 = (float)((Texture)val3).width / (float)((Texture)val3).height; float num3 = Mathf.Min(120f, 300f / num2); obj3.sizeDelta = new Vector2(0f, num3); Image obj4 = obj2.AddComponent(); obj4.sprite = Sprite.Create(val3, new Rect(0f, 0f, (float)((Texture)val3).width, (float)((Texture)val3).height), new Vector2(0.5f, 0.5f)); obj4.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); } _playerCountText = 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 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 b) => (Object)(object)b != (Object)null)); if ((Object)(object)val == (Object)null) { return null; } Component[] componentsInChildren = ((Component)val).GetComponentsInChildren(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) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)tmpTemplate != (Object)null) ? Object.Instantiate(tmpTemplate.gameObject, parent, false) : CreateChild(name, parent)); ((Object)val).name = name; val.SetActive(true); ContentSizeFitter component = val.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } LayoutElement component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { Object.Destroy((Object)(object)component2); } RectTransform obj = val.GetComponent() ?? val.AddComponent(); obj.anchorMin = new Vector2(0.05f, 1f); obj.anchorMax = new Vector2(0.95f, 1f); obj.pivot = new Vector2(0.5f, 1f); obj.anchoredPosition = new Vector2(0f, topOffset); obj.sizeDelta = new Vector2(0f, height); ((Transform)obj).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 obj2 = val.AddComponent(); obj2.font = Resources.GetBuiltinResource("Arial.ttf"); obj2.fontSize = Mathf.RoundToInt(fontSize); obj2.fontStyle = (FontStyle)(bold ? 1 : 0); ((Graphic)obj2).color = color; obj2.alignment = (TextAnchor)1; obj2.horizontalOverflow = (HorizontalWrapMode)0; obj2.verticalOverflow = (VerticalWrapMode)1; obj2.text = text; return (Component)(object)obj2; } 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 b) => (Object)(object)b != (Object)null)); if ((Object)(object)val != (Object)null) { GameObject obj = Object.Instantiate(((Component)val).gameObject, parent, false); ((Object)obj).name = "SG_ConnectBtn"; obj.SetActive(true); RectTransform component = obj.GetComponent(); 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