Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Valheim ServerGuard Client v1.8.1
Valheim-ServerGuard-Client.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
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