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 v2.1.0
Valheim-ServerGuard.dll
Decompiled a month 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.Http; 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 System.Threading.Tasks; using System.Timers; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; 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 - anti-cheat, mod allowlist and admin tools for Valheim. One DLL for both the dedicated server and the client.")] [assembly: AssemblyFileVersion("2.1.0.0")] [assembly: AssemblyInformationalVersion("2.1.0")] [assembly: AssemblyProduct("Valheim-ServerGuard")] [assembly: AssemblyTitle("Valheim-ServerGuard")] [assembly: AssemblyVersion("2.1.0.0")] namespace ValheimServerGuard { internal class ClientPlugin : MonoBehaviour { 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, object[] __args) { //IL_00a9: 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) { return; } 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(); } bool cheated = false; try { if (__args != null && __args.Length >= 5 && __args[4] is bool flag) { cheated = flag; } } catch { } Instance?.SendBuildPlace(text, pos, cheated); } 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_StripCheated", (Action<ZRpc, string>)delegate(ZRpc rpc, string ignoredCsv) { try { Instance?.OnStripCheatedReceived(ignoredCsv); } catch (Exception ex2) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] StripCheated 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)); } } }); peer.m_rpc.Register<string>("ServerGuard_ProgressPolicy", (Action<ZRpc, string>)delegate(ZRpc rpc, string payload) { try { OnProgressionPolicyReceived(payload); } catch (Exception ex2) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] ProgressPolicy handler error: " + ex2.Message)); } } }); peer.m_rpc.Register<string>("ServerGuard_ProgressState", (Action<ZRpc, string>)delegate(ZRpc rpc, string payload) { try { OnProgressStateReceived(payload); } catch (Exception ex2) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] ProgressState 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(); ResetProgression(); } 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), "IsCheatsEnabled")] public static class Patch_Terminal_IsCheatsEnabled { public static bool Prefix(ref bool __result) { try { if (!DevAccessGranted) { return true; } if (!IsActiveMultiplayerClient()) { return true; } __result = Terminal.m_cheat; return false; } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] IsCheatsEnabled patch error: " + ex.Message)); } return true; } } } [HarmonyPatch(typeof(ConsoleCommand), "IsValid")] public static class Patch_ConsoleCommand_IsValid { public static void Postfix(ConsoleCommand __instance, ref bool __result) { try { if (!__result && __instance != null && DevAccessGranted && IsActiveMultiplayerClient() && !__instance.RemoteCommand && IsDevCommandPermitted(__instance.Command) && (!__instance.IsCheat || Terminal.m_cheat) && (!__instance.IsNetwork || !((Object)(object)ZNet.instance == (Object)null))) { __result = true; } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] ConsoleCommand.IsValid patch 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(Minimap), "UpdateBiome")] public static class Patch_Minimap_UpdateBiome_StaffCoords { public static bool Prepare() { int num; if (AccessTools.Method(typeof(Minimap), "UpdateBiome", (Type[])null, (Type[])null) != null && MinimapBiomeLargeField != null) { num = ((MinimapScreenToWorld != null) ? 1 : 0); if (num != 0) { goto IL_0050; } } else { num = 0; } ManualLogSource logS = LogS; if (logS == null) { return (byte)num != 0; } logS.LogWarning((object)"[ServerGuard.Client] Minimap.UpdateBiome / m_biomeNameLarge / ScreenToWorldPoint not found — staff map coordinates disabled."); goto IL_0050; IL_0050: return (byte)num != 0; } public static void Postfix(Minimap __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) try { if (IsStaffClient && IsActiveMultiplayerClient() && !((Object)(object)__instance == (Object)null) && (int)__instance.m_mode == 2) { object value = MinimapBiomeLargeField.GetValue(__instance); if (value != null) { Vector3 val = (Vector3)(ZInput.IsMouseActive() ? ZInput.pointerPosition : new Vector3((float)Screen.width / 2f, (float)Screen.height / 2f)); Vector3 val2 = (Vector3)MinimapScreenToWorld.Invoke(__instance, new object[1] { val }); string text = (GetTmpProperty(value, "text") as string) ?? ""; string text2 = string.Format(CultureInfo.InvariantCulture, "<size=70%><color=#a0e0ff>X {0:F0} Z {1:F0}</color></size>", val2.x, val2.z); SetTmpProperty(value, "text", (text.Length > 0) ? (text + " " + text2) : text2); } } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Map coordinate overlay 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(); } } 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; } } [HarmonyPatch(typeof(ItemStand), "HaveAttachment")] public static class Patch_ItemStand_HaveAttachment_Personal { public static bool Prefix(ItemStand __instance, ref bool __result) { if (!IsPersonalAltar(__instance)) { return true; } __result = OwnsAltar(__instance); return false; } } [HarmonyPatch(typeof(ItemStand), "UpdateVisual")] public static class Patch_ItemStand_UpdateVisual_Personal { public static bool Prefix(ItemStand __instance) { if (!IsPersonalAltar(__instance)) { return true; } try { ApplyAltarVisual(__instance); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Altar visual failed: " + ex.Message)); } } return false; } } [HarmonyPatch(typeof(ItemStand), "RPC_SetVisualItem")] public static class Patch_ItemStand_RPC_SetVisualItem_Personal { public static bool Prefix(ItemStand __instance) { if (!IsPersonalAltar(__instance)) { return true; } try { ApplyAltarVisual(__instance); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Altar visual failed: " + ex.Message)); } } return false; } } [HarmonyPatch(typeof(ItemStand), "UseItem")] public static class Patch_ItemStand_UseItem_Personal { public static bool Prefix(ItemStand __instance, Humanoid user, ItemData item, ref bool __result) { if (!IsPersonalAltar(__instance) || (Object)(object)Instance == (Object)null) { return true; } try { __result = Instance.TryOfferPersonalTrophy(__instance, user, item); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Trophy offer failed: " + ex.Message)); } __result = true; } return false; } } [HarmonyPatch(typeof(BossStone), "DelayedAttachEffects_Step3")] public static class Patch_BossStone_Step3_Personal { public static bool Prefix(BossStone __instance) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || !IsPersonalAltar(__instance.m_itemStand)) { return true; } try { if (Object.op_Implicit((Object)(object)__instance.m_activeEffect)) { __instance.m_activeEffect.SetActive(true); } __instance.m_activateStep3.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1, default(ZDOID)); ((MonoBehaviour)__instance).StopCoroutine("FadeEmission"); ((MonoBehaviour)__instance).StartCoroutine("FadeEmission"); if (!string.IsNullOrEmpty(__instance.m_completedMessage)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, __instance.m_completedMessage, 0, (Sprite)null, false); } } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Altar effect failed: " + ex.Message)); } } return false; } } [HarmonyPatch(typeof(BossStone), "Start")] public static class Patch_BossStone_Start_Personal { public static void Prefix(BossStone __instance) { if ((Object)(object)__instance != (Object)null && IsPersonalAltar(__instance.m_itemStand)) { __instance.m_setsWorldKey = ""; } } } [HarmonyPatch(typeof(Player), "AddUniqueKey")] public static class Patch_Player_AddUniqueKey_Personal { public static bool Prefix(Player __instance, string name) { if (!_progressionActive || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (!IsProgressKeyClient(name) || _grantedKeys.Contains(name)) { return true; } ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)("[ServerGuard.Client] Not adding progress key '" + name + "' - the server did not credit this character.")); } return false; } } [HarmonyPatch(typeof(RandEventSystem), "PlayerIsReadyForEvent")] public static class Patch_RandEvent_PlayerIsReady_Personal { public static void Postfix(RandomEvent ev, ref bool __result) { if (!__result || !_progressionActive || ev == null || (Object)(object)ZoneSystem.instance == (Object)null || !IsActiveMultiplayerClient() || ev.m_altRequiredKnownItems.Count > 0 || ev.m_altRequiredNotKnownItems.Count > 0 || ev.m_altNotRequiredPlayerKeys.Count > 0 || ev.m_altRequiredPlayerKeysAny.Count > 0 || ev.m_altRequiredPlayerKeysAll.Count > 0) { return; } foreach (string requiredGlobalKey in ev.m_requiredGlobalKeys) { if (!ZoneSystem.instance.GetGlobalKey(requiredGlobalKey)) { __result = false; return; } } foreach (string notRequiredGlobalKey in ev.m_notRequiredGlobalKeys) { if (ZoneSystem.instance.GetGlobalKey(notRequiredGlobalKey)) { __result = false; break; } } } } [HarmonyPatch(typeof(ZoneSystem), "RPC_GlobalKeys")] public static class Patch_ZoneSystem_RPC_GlobalKeys_Personal { public static void Postfix() { if (!_progressionActive) { return; } try { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { localPlayer.UpdateEvents(); } } catch { } } } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Func<string, string> <>9__47_0; public static PopupButtonCallback <>9__52_0; public static Func<string, string> <>9__53_0; public static Func<string, string> <>9__53_1; public static Func<ModManifestEntry, KeyValuePair<string, string>> <>9__59_0; public static Func<ModManifestEntry, bool> <>9__60_0; public static Func<ModManifestEntry, bool> <>9__60_1; public static Func<ModManifestEntry, string> <>9__60_2; public static Comparison<KeyValuePair<string, int>> <>9__134_0; public static Func<ItemData, int> <>9__135_0; public static Func<ItemData, string> <>9__135_1; public static Func<string, string> <>9__139_0; public static Func<string, bool> <>9__139_1; public static Func<ItemData, string> <>9__140_0; public static MatchEvaluator <>9__155_0; public static MatchEvaluator <>9__156_0; public static Func<Button, bool> <>9__161_0; public static Func<Button, bool> <>9__164_0; public static Func<Assembly, Type> <>9__170_0; public static Func<Type, bool> <>9__170_1; public static Func<string, string> <>9__190_0; public static Func<string, bool> <>9__190_1; public static Func<string, string> <>9__191_0; public static Func<string, bool> <>9__191_1; public static Func<string, string> <>9__191_2; public static Func<string, bool> <>9__191_3; public static Func<string, string> <>9__194_0; public static Func<string, bool> <>9__198_0; internal string <ShowModeratorWelcome>b__47_0(string c) { return c; } internal void <ShowCheatTaintNotice>b__52_0() { ScaleUnifiedPopup(1f); try { UnifiedPopup.Pop(); } catch { } } internal string <AnnounceDevGrant>b__53_0(string c) { return c; } internal string <AnnounceDevGrant>b__53_1(string c) { return c; } internal KeyValuePair<string, string> <DeferredInit>b__59_0(ModManifestEntry m) { return new KeyValuePair<string, string>((!string.IsNullOrEmpty(m.Guid)) ? m.Guid : (m.Name ?? ""), m.Sha256 ?? ""); } internal bool <ExportAllowedModsSnippet>b__60_0(ModManifestEntry m) { return string.Equals(m.Guid, "com.taeguk.valheim.serverguard", StringComparison.OrdinalIgnoreCase); } internal bool <ExportAllowedModsSnippet>b__60_1(ModManifestEntry m) { return !string.Equals(m.Guid, "com.taeguk.valheim.serverguard", StringComparison.OrdinalIgnoreCase); } internal string <ExportAllowedModsSnippet>b__60_2(ModManifestEntry m) { return m.Name ?? ""; } internal int <SendCheatStateIfDue>b__134_0(KeyValuePair<string, int> a, KeyValuePair<string, int> b) { return string.CompareOrdinal(a.Key, b.Key); } internal int <OnStripCheatedReceived>b__135_0(ItemData i) { return Math.Max(1, i.m_stack); } internal string <OnStripCheatedReceived>b__135_1(ItemData i) { if (!((Object)(object)i.m_dropPrefab != (Object)null)) { return "?"; } return ((Object)i.m_dropPrefab).name; } internal string <OnRemoveItemsReceived>b__139_0(string s) { return s.Trim(); } internal bool <OnRemoveItemsReceived>b__139_1(string s) { return !string.IsNullOrEmpty(s); } internal string <RemoveItemsFromInventory>b__140_0(ItemData i) { return ((Object)i.m_dropPrefab).name; } internal string <FormatAnnouncementsRich>b__155_0(Match m) { string value = m.Groups[1].Value; string value2 = m.Groups[2].Value; if (!IsOpenableUrl(value2)) { return value; } return "<link=\"" + value2 + "\"><color=#7FB3FF><u>" + value + "</u></color></link>"; } internal string <FormatAnnouncementsPlain>b__156_0(Match m) { return m.Groups[1].Value + " (" + m.Groups[2].Value + ")"; } internal bool <GetMenuButtonLabelTemplate>b__161_0(Button b) { return (Object)(object)b != (Object)null; } internal bool <AddConnectButton>b__164_0(Button b) { return (Object)(object)b != (Object)null; } internal Type <LoadTexture>b__170_0(Assembly a) { try { return a.GetType("UnityEngine.ImageConversion"); } catch { return null; } } internal bool <LoadTexture>b__170_1(Type t) { return t != null; } internal string <OnProgressionPolicyReceived>b__190_0(string s) { return s.Trim().ToLowerInvariant(); } internal bool <OnProgressionPolicyReceived>b__190_1(string s) { return s.Length > 0; } internal string <OnProgressStateReceived>b__191_0(string s) { return s.Trim().ToLowerInvariant(); } internal bool <OnProgressStateReceived>b__191_1(string s) { return s.Length > 0; } internal string <OnProgressStateReceived>b__191_2(string s) { return s.Trim(); } internal bool <OnProgressStateReceived>b__191_3(string s) { return s.Length > 0; } internal string <SendProgressHelloIfDue>b__194_0(string k) { return SanitiseShort(k, 64).Replace(",", ""); } internal bool <SyncUniqueKeys>b__198_0(string u) { if (IsProgressKeyClient(u)) { return !_grantedKeys.Contains(u); } return false; } } public const string GUID = "com.taeguk.valheim.serverguard"; public const string NAME = "Valheim ServerGuard"; public const string VERSION = "2.1.0"; 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", "yesiuseddevcommandsbutiwantmyachievementsanyway", "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 string _cheatTaintServerPolicy = "0"; private static bool _cheatTaintNoticeShown = false; private static bool _moderatorWelcomeShown = false; private static string _devMode = "none"; private static HashSet<string> _devCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static string _devModeAnnounced = ""; private Coroutine _loginMessagesCo; private const float CheatTaintNoticeScale = 1.75f; private const float CheatTaintHeaderNet = 1.15f; private const float CheatTaintButtonNet = 1f; 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 CheatStatePollSeconds = 10f; private const float CheatStateHeartbeatSeconds = 60f; private string _lastCheatSignature; private float _lastCheatSentAt = -1000f; private static readonly FieldInfo MinimapBiomeLargeField = typeof(Minimap).GetField("m_biomeNameLarge", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo MinimapScreenToWorld = typeof(Minimap).GetMethod("ScreenToWorldPoint", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); 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 _progressionActive; private static bool _personalTemple; private static bool _altarsRequireKill; private static List<string> _progressPatterns = new List<string>(); private static Dictionary<string, string> _altarRequiredKey = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static HashSet<string> _grantedKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static HashSet<string> _myAltars = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static Player _progressHelloFor; private static Dictionary<string, string> _progressKeyCasing; private static readonly FieldInfo PlayerUniquesField = typeof(Player).GetField("m_uniques", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo ItemStandSetVisualItem = typeof(ItemStand).GetMethod("SetVisualItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static readonly MethodInfo BossStoneSetActivated = typeof(BossStone).GetMethod("SetActivated", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private static bool ConsoleGuardExempt => _consoleExempt; internal static bool IsOwnerClient => _consoleRole == "owner"; internal static bool IsStaffClient { get { if (!(_consoleRole == "owner")) { return _consoleRole == "moderator"; } return true; } } internal static bool DevAccessGranted { get { if (!(_devMode == "all")) { if (_devMode == "list") { return _devCommands.Count > 0; } return false; } return true; } } internal static bool IsDevCommandPermitted(string cmd) { if (string.IsNullOrEmpty(cmd)) { return false; } if (_devMode == "all") { return true; } if (_devMode == "list" && _devCommands.Count > 0) { if (!_devCommands.Contains(cmd)) { return string.Equals(cmd, "devcommands", StringComparison.OrdinalIgnoreCase); } return true; } return false; } 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] : ""); string text = ((array.Length > 6) ? array[6].Trim().ToLowerInvariant() : "none"); _devMode = ((text == "all" || text == "list") ? text : "none"); _devCommands = ToSet((array.Length > 7) ? array[7] : ""); if (_devMode == "list" && _devCommands.Count == 0) { _devMode = "none"; } _cheatTaintServerPolicy = ((array.Length > 8) ? array[8].Trim().ToLowerInvariant() : "0"); if (_cheatTaintServerPolicy.Length == 0) { _cheatTaintServerPolicy = "0"; } 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} " + "devcommands=" + _devMode + ((_devMode == "list") ? $"({_devCommands.Count})" : ""))); } ApplyBindPolicy(); AnnounceDevGrant(); Instance?.QueueLoginMessages(); } 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); _devMode = "none"; _devCommands = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _devModeAnnounced = ""; _cheatTaintServerPolicy = "0"; _moderatorWelcomeShown = false; } private void QueueLoginMessages() { try { if (IsActiveMultiplayerClient()) { bool num = _consoleRole == "moderator" && !_moderatorWelcomeShown; bool flag = _cheatTaintServerPolicy != "0" && !_cheatTaintNoticeShown; if ((num || flag) && _loginMessagesCo == null) { _loginMessagesCo = ((MonoBehaviour)this).StartCoroutine(LoginMessagesCoroutine()); } } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] QueueLoginMessages error: " + ex.Message)); } } } private IEnumerator LoginMessagesCoroutine() { for (float waited = 0f; waited < 180f; waited += 0.5f) { if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)Chat.instance == (Object)null)) { break; } yield return (object)new WaitForSeconds(0.5f); } _loginMessagesCo = null; if ((Object)(object)Player.m_localPlayer == (Object)null || !IsActiveMultiplayerClient()) { yield break; } yield return (object)new WaitForSeconds(2f); try { if (_consoleRole == "moderator" && !_moderatorWelcomeShown) { _moderatorWelcomeShown = true; ShowModeratorWelcome(); } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Moderator welcome error: " + ex.Message)); } } try { if (_cheatTaintServerPolicy != "0" && !_cheatTaintNoticeShown) { _cheatTaintNoticeShown = true; ShowCheatTaintNotice(_cheatTaintServerPolicy); } } catch (Exception ex2) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] Cheat-taint notice error: " + ex2.Message)); } } } private static void ShowModeratorWelcome() { Chat instance = Chat.instance; if ((Object)(object)instance == (Object)null) { return; } string text = "Viking"; try { Player localPlayer = Player.m_localPlayer; text = ((localPlayer != null) ? localPlayer.GetPlayerName() : null) ?? text; } catch { } string text2 = ((_devMode == "list" && _devCommands.Count > 0) ? string.Join(", ", _devCommands.OrderBy((string c) => c)) : "(none granted right now)"); string[] array = new string[5] { "<color=#ffd700>[ServerGuard]</color> Welcome back, <color=#ffd700>" + text + "</color>. You are a <color=#ffd700>moderator</color> on this server.", "Dev commands available to you: <color=#a0e0ff>" + text2 + "</color>", "Type <color=#a0e0ff>devcommands</color> in the F5 console to enable them. Anything not on this list is refused and reported to the owner.", "Moderator tools: <color=#a0e0ff>sg help</color> in the console (kick, ban, whois, build log).", "Please moderate responsibly. Your actions are logged and posted to the admin channel, and the same cheat-detection rules that apply to players apply to you. Use your commands to help players, never to gain an advantage." }; foreach (string text3 in array) { try { ((Terminal)instance).AddString(text3); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)("[ServerGuard.Client] " + text3 + " (" + ex.Message + ")")); } } } try { typeof(Chat).GetField("m_hideTimer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.SetValue(instance, 0f); } catch { } } private static void ScaleUnifiedPopup(float scale) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: 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) try { object obj = typeof(UnifiedPopup).GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); if (obj == null) { return; } object? obj2 = typeof(UnifiedPopup).GetField("popupUIParent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); GameObject val = (GameObject)((obj2 is GameObject) ? obj2 : null); if (!((Object)(object)val == (Object)null)) { val.transform.localScale = new Vector3(scale, scale, 1f); bool num = Math.Abs(scale - 1f) < 0.001f; float num2 = (num ? 1f : (1.15f / scale)); float num3 = (num ? 1f : (1f / scale)); object? obj3 = typeof(UnifiedPopup).GetField("headerText", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); Component val2 = (Component)((obj3 is Component) ? obj3 : null); if (val2 != null) { val2.transform.localScale = new Vector3(num2, num2, 1f); } object? obj4 = typeof(UnifiedPopup).GetField("buttonCenter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); Component val3 = (Component)((obj4 is Component) ? obj4 : null); if (val3 != null) { val3.transform.localScale = new Vector3(num3, num3, 1f); } } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Popup scale failed: " + ex.Message)); } } } private static void ShowCheatTaintNotice(string policy) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown string text = ((policy == "strip") ? "Flagged items and builds are reported to the server staff automatically, and flagged items are removed from your inventory." : ((policy == "violation") ? "Flagged items and builds are reported to the server staff automatically, flagged items are removed from your inventory, and each occurrence counts as a strike toward a ban." : "Flagged items and builds are reported to the server staff automatically, and depending on server policy flagged items may be removed from your inventory or count as a strike toward a ban.")); string text2 = "Valheim marks every item, build and creature that comes from a cheat command — spawned items, anything crafted from them, pieces built for free, kills made in god or fly mode. The mark is saved with your character, so it follows gear brought in from single-player.\n\nThis server reads those marks. " + text + "\n\nIf you have used cheats on this character elsewhere, don't bring that gear here. Play fair and none of this will ever concern you."; try { if (UnifiedPopup.IsAvailable()) { object obj = <>c.<>9__52_0; if (obj == null) { PopupButtonCallback val = delegate { ScaleUnifiedPopup(1f); try { UnifiedPopup.Pop(); } catch { } }; <>c.<>9__52_0 = val; obj = (object)val; } UnifiedPopup.Push((PopupBase)new WarningPopup("Cheat detection is active on this server", text2, (PopupButtonCallback)obj, false)); ScaleUnifiedPopup(1.75f); return; } } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Notice popup unavailable (" + ex.Message + "); falling back to chat.")); } } try { Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString("<color=#ffd700>[ServerGuard]</color> Cheat detection is active on this server. " + text2.Replace("\n\n", " ")); } } catch { } } private static void AnnounceDevGrant() { try { string text = ((_devMode == "list") ? ("list:" + string.Join(",", _devCommands.OrderBy((string c) => c))) : _devMode); if (text == _devModeAnnounced) { return; } _devModeAnnounced = text; if (_devMode == "all") { Instance?.DisplayAdminReply("[ServerGuard] Dev commands unlocked for you (owner). Type `devcommands` to enable them."); } else if (_devMode == "list") { Instance?.DisplayAdminReply("[ServerGuard] Dev commands available to you (moderator): " + string.Join(", ", _devCommands.OrderBy((string c) => c)) + ". Type `devcommands` to enable them."); } } catch { } } private void Awake() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown Instance = this; LogS = ServerGuardPlugin.Log; EnsureConfig(); _harmony = new Harmony("com.taeguk.valheim.serverguard.client"); int num = ServerGuardPlugin.PatchNested(_harmony, typeof(ClientPlugin)); LogS.LogInfo((object)$"[ServerGuard.Client] Applied {num} client-side Harmony patch class(es)."); ((MonoBehaviour)this).StartCoroutine(DeferredInit()); } private IEnumerator DeferredInit() { yield return null; yield return (object)new WaitForSeconds(2f); BuildManifestCache(); ExportAllowedModsSnippet(); ((MonoBehaviour)this).StartCoroutine(SkillReportLoop()); ((MonoBehaviour)this).StartCoroutine(CheatStateLoop()); ((MonoBehaviour)this).StartCoroutine(ProgressionHelloLoop()); 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}", "2.1.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<ModManifestEntry> list = _cachedManifest ?? new List<ModManifestEntry>(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# ServerGuard - allowed_mods snippet generated by ServerGuard.Client v2.1.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 <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("# ServerGuard itself (this DLL, the same mod the server runs) is intentionally listed"); stringBuilder.AppendLine("# under required_mods, NOT allowed_mods - the server demands its presence."); stringBuilder.AppendLine(); ModManifestEntry modManifestEntry = list.FirstOrDefault((ModManifestEntry m) => string.Equals(m.Guid, "com.taeguk.valheim.serverguard", 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 # Valheim ServerGuard v2.1.0"); } stringBuilder.AppendLine(); stringBuilder.AppendLine("allowed_mods:"); List<ModManifestEntry> list2 = list.Where((ModManifestEntry m) => !string.Equals(m.Guid, "com.taeguk.valheim.serverguard", 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, bool cheated = false) { //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}|{4}", text, pos.x, pos.y, pos.z, cheated ? "1" : "0"); _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 switch { "moderator" => "this dev command is not in the server's moderator list", "notallowed" => "this server only permits a whitelist of console commands", "bind" => "key binds are disabled on this server", _ => "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 IsRegisteredServerOnlyCommand(string cmd) { object obj = LookupCommandObject(cmd); if (obj == null) { return false; } if (!ReadBoolMember(obj, new string[2] { "OnlyServer", "onlyServer" })) { return ReadBoolMember(obj, new string[2] { "OnlyAdmin", "onlyAdmin" }); } return true; } private static bool IsDevOnlyCommand(string cmd) { if (!CheatCommands.Contains(cmd) && !IsRegisteredCheatCommand(cmd)) { return IsRegisteredServerOnlyCommand(cmd); } return true; } 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 (_devMode == "list" && IsDevOnlyCommand(cmd)) { if (IsDevCommandPermitted(cmd)) { return false; } category = "moderator"; return true; } 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)); } } } private IEnumerator CheatStateLoop() { yield return (object)new WaitForSeconds(15f); while (true) { yield return (object)new WaitForSeconds(10f); try { SendCheatStateIfDue(force: false); } catch (Exception ex) { ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)("[ServerGuard.Client] Cheat state tick error: " + ex.Message)); } } } } private void SendCheatStateIfDue(bool force) { if (_serverRpc == null || !IsActiveMultiplayerClient()) { return; } Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return; } bool flag = false; try { flag = (Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile().m_usedCheats; } catch { } bool flag2 = false; try { flag2 = PlayerProfile.s_bypassCheatChecks; } catch { } List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(); foreach (ItemData allItem in val.GetAllItems()) { if (allItem != null && allItem.m_cheated) { string s = (((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : (allItem.m_shared?.m_name ?? "unknown")); s = SanitiseShort(s, 48).Replace(':', '_').Replace(',', '_').Replace('|', '_'); list.Add(new KeyValuePair<string, int>(s, Math.Max(1, allItem.m_stack))); } } list.Sort((KeyValuePair<string, int> a, KeyValuePair<string, int> b) => string.CompareOrdinal(a.Key, b.Key)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(flag ? '1' : '0').Append('|'); stringBuilder.Append(flag2 ? '1' : '0').Append('|'); stringBuilder.Append(list.Count).Append('|'); for (int num = 0; num < list.Count; num++) { if (num > 0) { stringBuilder.Append(','); } stringBuilder.Append(list[num].Key).Append(':').Append(list[num].Value); } string text = stringBuilder.ToString(); float realtimeSinceStartup = Time.realtimeSinceStartup; bool flag3 = !string.Equals(text, _lastCheatSignature, StringComparison.Ordinal); if (!force && !flag3 && realtimeSinceStartup - _lastCheatSentAt < 60f) { return; } try { _serverRpc.Invoke("ServerGuard_CheatState", new object[1] { text }); _lastCheatSignature = text; _lastCheatSentAt = realtimeSinceStartup; if (flag3 && list.Count > 0) { ManualLogSource logS = LogS; if (logS != null) { logS.LogInfo((object)$"[ServerGuard.Client] Reported {list.Count} cheat-flagged inventory item(s) to the server."); } } } catch (Exception ex) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] Cheat state send failed: " + ex.Message)); } } } internal void OnStripCheatedReceived(string ignoredCsv) { try { Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val == null) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); string[] array = (ignoredCsv ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { hashSet.Add(text); } } List<ItemData> list = new List<ItemData>(); foreach (ItemData allItem in val.GetAllItems()) { if (allItem != null && allItem.m_cheated) { string text2 = (((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : ""); if (text2.Length <= 0 || !hashSet.Contains(text2)) { list.Add(allItem); } } } if (list.Count == 0) { return; } foreach (ItemData item in list) { val.RemoveItem(item); } int num = list.Sum((ItemData val2) => Math.Max(1, val2.m_stack)); ManualLogSource logS = LogS; if (logS != null) { logS.LogWarning((object)($"[ServerGuard.Client] Removed {num} cheat-flagged item(s): " + string.Join(", ", list.Select((ItemData val2) => (!((Object)(object)val2.m_dropPrefab != (Object)null)) ? "?" : ((Object)val2.m_dropPrefab).name)))); } try { ((Character)Player.m_localPlayer).Message((MessageType)2, $"[ServerGuard] {num} cheat-spawned item(s) removed by server policy", 0, (Sprite)null, false); } catch { } SendCheatStateIfDue(force: true); } catch (Exception ex) { ManualLogSource logS2 = LogS; if (logS2 != null) { logS2.LogWarning((object)("[ServerGuard.Client] OnStripCheatedReceived error: " + 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)); }