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 GyattAntiCheat v1.3.1
GyattAntiCheat.dll
Decompiled 2 weeks agousing System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("GyattAntiCheat")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("GyattAntiCheat")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("1eb59790-439f-447a-a5d8-4e23477b83d0")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] namespace GyattAntiCheat; [BepInPlugin("com.valheim.gyattanticheat", "GyattAntiCheat", "1.3.1")] public class GyattAntiCheatPlugin : BaseUnityPlugin { [HarmonyPatch(typeof(Skills), "RaiseSkill")] public static class Skills_RaiseSkill_Patch { private static void Postfix(Skills __instance, SkillType skillType) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_018d: 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_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)__instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || IsLocalAdmin || (Object)(object)__instance != (Object)(object)((Character)Player.m_localPlayer).GetSkills()) { return; } Skill val = ((IEnumerable<Skill>)__instance.GetSkillList()).FirstOrDefault((Func<Skill, bool>)((Skill s) => s.m_info != null && s.m_info.m_skill == skillType)); if (val == null) { return; } if (LastSkillLevels.TryGetValue(skillType, out var value) && val.m_level > value) { float time = Time.time; if (LastSkillLevelUpTimes.TryGetValue(skillType, out var value2) && val.m_level > 10f && time - value2 < 1.5f) { _violationQueue.Enqueue($"Fast Skill Level Up Trainer Detected ({val.m_info.m_skill}: Lv {value:F0} -> {val.m_level:F0} in {time - value2:F1}s)"); } LastSkillLevelUpTimes[skillType] = time; } LastSkillLevels[skillType] = val.m_level; if (val.m_level > 100.1f) { _violationQueue.Enqueue($"Illegal Skill Level ({val.m_info.m_skill}: {val.m_level:F0} > 100)"); } } } private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); [HarmonyPatch(typeof(ZNet), "OnNewConnection")] public static class ZNet_OnNewConnection_Patch { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static Action<ZRpc, bool> <>9__0_2; public static Action<ZRpc, string> <>9__0_3; public static Method <>9__0_4; internal void <Postfix>b__0_2(ZRpc rpc, bool isAdmin) { OnClientReceiveAdminStatus(isAdmin); } internal void <Postfix>b__0_3(ZRpc rpc, string msg) { OnAdminReceiveNotification(msg); } internal void <Postfix>b__0_4(ZRpc rpc) { TriggerClientSave(); } } private static void Postfix(ZNet __instance, ZNetPeer peer) { //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown if (peer == null || peer.m_rpc == null) { return; } if (__instance.IsServer()) { CacheServerPluginFiles(); peer.m_rpc.Register<string, string>("GyattAC_Handshake", (Action<ZRpc, string, string>)delegate(ZRpc val2, string pName, string mods) { OnServerReceiveHandshake(peer, pName, mods); }); peer.m_rpc.Register<string, string>("GyattAC_ReportViolation", (Action<ZRpc, string, string>)delegate(ZRpc val2, string pName, string reason) { OnServerReceiveViolation(peer, pName, reason); }); if (RequireModToJoin.Value) { ((MonoBehaviour)__instance).StartCoroutine(CheckPlayerHandshake(peer)); } return; } peer.m_rpc.Register<bool>("GyattAC_SetAdminStatus", (Action<ZRpc, bool>)delegate(ZRpc val2, bool isAdmin) { OnClientReceiveAdminStatus(isAdmin); }); peer.m_rpc.Register<string>("GyattAC_NotifyAdmin", (Action<ZRpc, string>)delegate(ZRpc val2, string msg) { OnAdminReceiveNotification(msg); }); ZRpc rpc = peer.m_rpc; object obj = <>c.<>9__0_4; if (obj == null) { Method val = delegate { TriggerClientSave(); }; <>c.<>9__0_4 = val; obj = (object)val; } rpc.Register("GyattAC_ForceSave", (Method)obj); } private static IEnumerator CheckPlayerHandshake(ZNetPeer peer) { yield return (object)new WaitForSeconds(30f); if (peer != null && peer.IsReady() && !IsPeerAdmin(peer)) { string steamID = peer.m_socket.GetHostName(); if (!VerifiedPeers.Contains(steamID)) { Log.LogWarning((object)("[VIOLATION] Kicking '" + peer.m_playerName + "' (ID: " + steamID + ") - Missing GyattAntiCheat client mod.")); ZNet.instance.Kick(peer.m_playerName); } } } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] public static class ZNet_RPC_PeerInfo_Patch { private static void Postfix(ZNet __instance, ZRpc rpc) { if (__instance.IsServer()) { return; } List<string> list = new List<string>(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { list.Add(value.Metadata.GUID + "|" + value.Metadata.Name); } string text = string.Join(";", list); string text2 = (((Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile() != null) ? Game.instance.GetPlayerProfile().GetName() : "Player"); rpc.Invoke("GyattAC_Handshake", new object[2] { text2, text }); } } [HarmonyPatch(typeof(Game), "Logout")] public static class Game_Logout_Patch { private static void Postfix() { VerifiedPeers.Clear(); _loggedViolations.Clear(); _pendingKicks.Clear(); LastSkillLevels.Clear(); LastSkillLevelUpTimes.Clear(); IsLocalAdmin = false; } } public const string ModGUID = "com.valheim.gyattanticheat"; public const string ModName = "GyattAntiCheat"; public const string ModVersion = "1.3.1"; public static ManualLogSource Log; private Harmony _harmony; public static ConfigEntry<bool> EnforceExactServerMods; public static ConfigEntry<bool> CheckMissingServerMods; public static ConfigEntry<string> AllowedClientOnlyMods; public static ConfigEntry<string> ServerOnlyMods; public static ConfigEntry<string> BlacklistedWords; public static ConfigEntry<bool> AutoKick; public static ConfigEntry<bool> RequireModToJoin; private static readonly FieldInfo fNoPlacementCost = AccessTools.Field(typeof(Player), "m_noPlacementCost"); private static readonly ConcurrentQueue<string> _violationQueue = new ConcurrentQueue<string>(); private static bool _isScanningProcesses = false; public static bool IsLocalAdmin = false; private float _stateCheckTimer = 0f; private float _processCheckTimer = 0f; public static HashSet<string> VerifiedPeers = new HashSet<string>(); private static HashSet<string> _loggedViolations = new HashSet<string>(); private static HashSet<string> _pendingKicks = new HashSet<string>(); private static List<string> _serverPluginFilesData = null; public static Dictionary<SkillType, float> LastSkillLevels = new Dictionary<SkillType, float>(); public static Dictionary<SkillType, float> LastSkillLevelUpTimes = new Dictionary<SkillType, float>(); private void Awake() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; EnforceExactServerMods = ((BaseUnityPlugin)this).Config.Bind<bool>("ModSync", "EnforceServerModWhitelist", true, "If true, players cannot use any extra mod that is not installed on the server (except AllowedClientOnlyMods)."); CheckMissingServerMods = ((BaseUnityPlugin)this).Config.Bind<bool>("ModSync", "CheckMissingServerMods", true, "If true, players must have all active mods that are loaded on the server (except ServerOnlyMods)."); AllowedClientOnlyMods = ((BaseUnityPlugin)this).Config.Bind<string>("ModSync", "AllowedClientOnlyMods", "org.bepinex.plugins.configurationmanager,com.bepinex.configurationmanager", "Comma-separated list of Mod GUIDs/Names allowed on clients even if not installed on the server."); ServerOnlyMods = ((BaseUnityPlugin)this).Config.Bind<string>("ModSync", "ServerOnlyMods", "discordconnector,server_devcommands", "Comma-separated list of Mod GUIDs/Names installed ONLY on the server that clients are not required to have."); BlacklistedWords = ((BaseUnityPlugin)this).Config.Bind<string>("Security", "BlacklistedKeywords", "wemod,cheatengine,cheat,trainer,fling,mrantifun,sktoolbox,speedhack,xenos,injector,artmoney", "Comma-separated list of restricted process names, window titles, or mod keywords."); AutoKick = ((BaseUnityPlugin)this).Config.Bind<bool>("Security", "AutoKickCheater", true, "Automatically kick non-admin players when a violation or mod mismatch is detected (Server only)."); RequireModToJoin = ((BaseUnityPlugin)this).Config.Bind<bool>("Security", "RequireGyattAntiCheat", true, "Require clients to have GyattAntiCheat installed to stay connected (Server only)."); _harmony = new Harmony("com.valheim.gyattanticheat"); _harmony.PatchAll(); Log.LogInfo((object)"GyattAntiCheat v1.3.1 Loaded!"); } private static void CacheServerPluginFiles() { if (_serverPluginFilesData != null) { return; } _serverPluginFilesData = new List<string>(); try { if (!Directory.Exists(Paths.PluginPath)) { return; } string[] files = Directory.GetFiles(Paths.PluginPath, "*.dll", SearchOption.AllDirectories); string[] array = files; foreach (string path in array) { try { _serverPluginFilesData.Add(Path.GetFileNameWithoutExtension(path).ToLowerInvariant()); byte[] bytes = File.ReadAllBytes(path); _serverPluginFilesData.Add(Encoding.ASCII.GetString(bytes).ToLowerInvariant()); } catch { } } Log.LogInfo((object)$"[MOD SYNC] Cached {files.Length} plugin DLL files from server plugins directory."); } catch (Exception ex) { Log.LogWarning((object)("[MOD SYNC] Failed to scan plugin directory: " + ex.Message)); } } private static bool IsModInServerPluginFolder(string guid, string name) { CacheServerPluginFiles(); string value = guid.ToLowerInvariant(); string value2 = name.ToLowerInvariant(); foreach (string serverPluginFilesDatum in _serverPluginFilesData) { if (serverPluginFilesDatum.Contains(value) || serverPluginFilesDatum.Contains(value2)) { return true; } } return false; } private void Update() { if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null) { return; } if (IsLocalAdmin || ZNet.instance.IsServer()) { string result; while (_violationQueue.TryDequeue(out result)) { } return; } string result2; while (_violationQueue.TryDequeue(out result2)) { TriggerClientSave(); SendViolationToServer(result2); } _stateCheckTimer += Time.deltaTime; if (_stateCheckTimer >= 3f) { _stateCheckTimer = 0f; CheckLightweightPlayerState(Player.m_localPlayer); } _processCheckTimer += Time.deltaTime; if (_processCheckTimer >= 30f) { _processCheckTimer = 0f; RunBackgroundProcessScan(); } } private void CheckLightweightPlayerState(Player p) { if (((Character)p).IsDead()) { return; } if (((Character)p).InGodMode()) { _violationQueue.Enqueue("God Mode Active (InGodMode)"); } if (((Character)p).InGhostMode()) { _violationQueue.Enqueue("Ghost Mode Active"); } if (((Character)p).IsDebugFlying() || ((Character)p).m_flying) { _violationQueue.Enqueue("Debug Fly / Free Fly Active"); } if (fNoPlacementCost != null && (bool)fNoPlacementCost.GetValue(p)) { _violationQueue.Enqueue("No Crafting Req / No Placement Cost Active"); } if (p.GetMaxCarryWeight() > 1200f) { _violationQueue.Enqueue($"Illegal Carry Weight ({p.GetMaxCarryWeight():F0} > 1200)"); } float health = ((Character)p).GetHealth(); float maxHealth = ((Character)p).GetMaxHealth(); if (health > 450f || maxHealth > 450f) { _violationQueue.Enqueue($"Illegal HP Stat via Trainer ({health:F0}/{maxHealth:F0})"); } else if (health > maxHealth + 10f) { _violationQueue.Enqueue($"Frozen Health Cheat Detected ({health:F0}/{maxHealth:F0})"); } float stamina = p.GetStamina(); float maxStamina = ((Character)p).GetMaxStamina(); if (stamina > 380f || maxStamina > 380f) { _violationQueue.Enqueue($"Illegal Stamina Stat via Trainer ({stamina:F0}/{maxStamina:F0})"); } else if (stamina > maxStamina + 10f) { _violationQueue.Enqueue($"Frozen Stamina Cheat Detected ({stamina:F0}/{maxStamina:F0})"); } float eitr = p.GetEitr(); float maxEitr = ((Character)p).GetMaxEitr(); if (maxEitr <= 0f && eitr > 5f) { _violationQueue.Enqueue($"Illegal Eitr Generated Without Food ({eitr:F0})"); } else if (eitr > 350f || maxEitr > 350f) { _violationQueue.Enqueue($"Illegal Eitr Stat via Trainer ({eitr:F0}/{maxEitr:F0})"); } else if (maxEitr > 0f && eitr > maxEitr + 10f) { _violationQueue.Enqueue($"Frozen Eitr Cheat Detected ({eitr:F0}/{maxEitr:F0})"); } if (((Humanoid)p).GetInventory() != null) { foreach (ItemData allItem in ((Humanoid)p).GetInventory().GetAllItems()) { if (allItem != null && allItem.m_shared != null && allItem.m_shared.m_useDurability) { float maxDurability = allItem.GetMaxDurability(); if (allItem.m_durability > maxDurability + 10f) { _violationQueue.Enqueue($"Infinite Durability Cheat ({allItem.m_shared.m_name}: {allItem.m_durability:F0}/{maxDurability:F0})"); break; } } } } if (p.GetFoods() != null) { foreach (Food food in p.GetFoods()) { if (food.m_time > 3300f) { _violationQueue.Enqueue($"Infinite Food Duration Cheat ({food.m_time:F0}s > 3300s)"); break; } } } if (((Character)p).GetSEMan() == null) { return; } foreach (StatusEffect statusEffect in ((Character)p).GetSEMan().GetStatusEffects()) { if ((Object)(object)statusEffect != (Object)null && ((Object)statusEffect).name.Contains("Rested") && statusEffect.GetRemaningTime() > 2700f) { _violationQueue.Enqueue($"Infinite Rested Duration Cheat ({statusEffect.GetRemaningTime():F0}s > 2700s)"); break; } } } [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Unicode)] private static extern int GetWindowText(IntPtr hWnd, StringBuilder strText, int maxCount); [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd); private void RunBackgroundProcessScan() { if (_isScanningProcesses || IsLocalAdmin) { return; } _isScanningProcesses = true; string text = BlacklistedWords.Value.Replace("cheat,", "cheat engine,").Replace(",cheat", ",cheat engine"); string[] badWords = (from s in (text + ",fling").ToLower().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim()).ToArray(); Task.Run(delegate { try { EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) { if (IsWindowVisible(hWnd)) { StringBuilder stringBuilder = new StringBuilder(256); if (GetWindowText(hWnd, stringBuilder, 256) > 0) { string text3 = stringBuilder.ToString().ToLower(); if (text3.Contains("anti-cheat") || text3.Contains("anticheat") || text3.Contains("google chrome") || text3.Contains("edge") || text3.Contains("firefox") || text3.Contains("opera") || text3.Contains("brave")) { return true; } string[] array3 = badWords; foreach (string value2 in array3) { if (text3.Contains(value2)) { _violationQueue.Enqueue("Restricted Window Detected: " + stringBuilder.ToString()); return false; } } } } return true; }, IntPtr.Zero); Process[] processes = Process.GetProcesses(); Process[] array = processes; foreach (Process process in array) { try { string text2 = process.ProcessName.ToLower(); if (!text2.Contains("chrome") && !text2.Contains("msedge") && !text2.Contains("firefox") && !text2.Contains("opera") && !text2.Contains("brave")) { string[] array2 = badWords; foreach (string value in array2) { if (text2.Contains(value)) { _violationQueue.Enqueue("Restricted Process Detected: " + process.ProcessName); break; } } } } catch { } finally { process.Dispose(); } } } catch { } finally { _isScanningProcesses = false; } }); } private void SendViolationToServer(string reason) { if (!((Object)(object)ZNet.instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && !IsLocalAdmin) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null && serverPeer.m_rpc != null) { serverPeer.m_rpc.Invoke("GyattAC_ReportViolation", new object[2] { Player.m_localPlayer.GetPlayerName(), reason }); } } } private static void TriggerClientSave() { try { if ((Object)(object)Game.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null) { Game.instance.SavePlayerProfile(false, false); Log.LogInfo((object)"[GYATT-AC] Profile saved to prevent item duplication."); } } catch { } } private static void OnClientReceiveAdminStatus(bool isAdmin) { IsLocalAdmin = isAdmin; if (isAdmin) { Log.LogInfo((object)"[GYATT-AC] Admin privileges granted by server. Anti-cheat checks bypassed."); } } private static bool IsPeerAdmin(ZNetPeer peer) { if (peer == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.GetAdminList() == null) { return false; } string hostName = peer.m_socket.GetHostName(); string cleanId = hostName.Replace("Steam_", "").Trim(); if (string.IsNullOrEmpty(cleanId)) { return false; } List<string> adminList = ZNet.instance.GetAdminList(); return adminList.Any((string entry) => !string.IsNullOrWhiteSpace(entry) && entry.Trim().Contains(cleanId)); } private static void OnServerReceiveHandshake(ZNetPeer peer, string playerName, string modListRaw) { if (!ZNet.instance.IsServer() || peer == null) { return; } string hostName = peer.m_socket.GetHostName(); VerifiedPeers.Add(hostName); if (string.IsNullOrEmpty(playerName) && !string.IsNullOrEmpty(peer.m_playerName)) { playerName = peer.m_playerName; } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); string[] array = modListRaw.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { string[] array2 = text.Split(new char[1] { '|' }); if (array2.Length >= 2) { dictionary[array2[0].Trim()] = array2[1].Trim(); } else { dictionary[text.Trim()] = text.Trim(); } } Dictionary<string, string> dictionary2 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { dictionary2[value.Metadata.GUID] = value.Metadata.Name; } if (IsPeerAdmin(peer)) { Log.LogInfo((object)string.Format("[ADMIN JOIN] Admin '{0}' (ID: {1}) connected with {2} mods: [{3}] -> Bypassed all checks.", playerName, hostName, dictionary.Count, string.Join(", ", dictionary.Values))); ZRpc rpc = peer.m_rpc; if (rpc != null) { rpc.Invoke("GyattAC_SetAdminStatus", new object[1] { true }); } return; } Log.LogInfo((object)string.Format("[PLAYER JOIN] Player '{0}' (ID: {1}) connected with {2} mods: [{3}]", playerName, hostName, dictionary.Count, string.Join(", ", dictionary.Values))); string[] source = (from s in AllowedClientOnlyMods.Value.ToLower().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim()).ToArray(); string[] source2 = (from s in ServerOnlyMods.Value.ToLower().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries) select s.Trim()).ToArray(); if (EnforceExactServerMods.Value) { List<string> list = new List<string>(); foreach (KeyValuePair<string, string> item in dictionary) { string guid = item.Key; string name = item.Value; if (!dictionary2.ContainsKey(guid) && !IsModInServerPluginFolder(guid, name) && !source.Any((string allow) => guid.ToLower().Contains(allow) || name.ToLower().Contains(allow))) { list.Add(name + " (" + guid + ")"); } } if (list.Count > 0) { OnServerReceiveViolation(peer, playerName, "Unauthorized Extra Mods (Not on Server): " + string.Join(", ", list)); return; } } if (CheckMissingServerMods.Value) { List<string> list2 = new List<string>(); foreach (KeyValuePair<string, string> item2 in dictionary2) { string guid2 = item2.Key; string name2 = item2.Value; if (!dictionary.ContainsKey(guid2) && !source2.Any((string srvOnly) => guid2.ToLower().Contains(srvOnly) || name2.ToLower().Contains(srvOnly))) { list2.Add(name2 + " (" + guid2 + ")"); } } if (list2.Count > 0) { OnServerReceiveViolation(peer, playerName, "Missing Required Server Mods: " + string.Join(", ", list2)); return; } } string[] array3 = BlacklistedWords.Value.ToLower().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (KeyValuePair<string, string> item3 in dictionary) { if (dictionary2.ContainsKey(item3.Key) || IsModInServerPluginFolder(item3.Key, item3.Value)) { continue; } string text2 = (item3.Value + " " + item3.Key).ToLower(); string[] array4 = array3; foreach (string text3 in array4) { if (!string.IsNullOrWhiteSpace(text3) && text2.Contains(text3.Trim())) { OnServerReceiveViolation(peer, playerName, "Blacklisted Mod Installed: " + item3.Value); return; } } } Log.LogInfo((object)("[MOD SYNC OK] Player '" + playerName + "' (ID: " + hostName + ") passed all mod verification checks.")); } private static void OnServerReceiveViolation(ZNetPeer offenderPeer, string playerName, string violationReason) { if (!ZNet.instance.IsServer() || offenderPeer == null || IsPeerAdmin(offenderPeer)) { return; } string hostName = offenderPeer.m_socket.GetHostName(); if (string.IsNullOrEmpty(playerName)) { playerName = offenderPeer.m_playerName; } string item = hostName + "_" + violationReason; if (!_loggedViolations.Contains(item)) { _loggedViolations.Add(item); string text = "[VIOLATION] Player: '" + playerName + "' (ID: " + hostName + ") | Reason: " + violationReason; Log.LogWarning((object)text); OnAdminReceiveNotification(text); foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (IsPeerAdmin(peer)) { ZRpc rpc = peer.m_rpc; if (rpc != null) { rpc.Invoke("GyattAC_NotifyAdmin", new object[1] { text }); } } } } if (AutoKick.Value && !_pendingKicks.Contains(hostName)) { _pendingKicks.Add(hostName); Log.LogWarning((object)("[ACTION] Triggering save and delaying kick for '" + playerName + "' (ID: " + hostName + ") to prevent dupe...")); ZRpc rpc2 = offenderPeer.m_rpc; if (rpc2 != null) { rpc2.Invoke("GyattAC_ForceSave", Array.Empty<object>()); } ((MonoBehaviour)ZNet.instance).StartCoroutine(ExecuteDelayedKick(offenderPeer, playerName, hostName)); } } private static IEnumerator ExecuteDelayedKick(ZNetPeer peer, string playerName, string steamID) { yield return (object)new WaitForSeconds(2f); _pendingKicks.Remove(steamID); if (peer != null && (Object)(object)ZNet.instance != (Object)null) { Log.LogWarning((object)("[ACTION] Kicking '" + playerName + "' (ID: " + steamID + ") now (Save complete).")); ZNet.instance.Kick(playerName); } } private static void OnAdminReceiveNotification(string message) { if ((IsLocalAdmin || (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())) && (Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)1, "<color=red>" + message + "</color>", 0, (Sprite)null, false); Chat instance = Chat.instance; if (instance != null) { ((Terminal)instance).AddString("[GYATT-AC]", "<color=red>" + message + "</color>", (Type)1, false); } } } }