////credits and licenses in the resources folder
//using AmongUs.GameOptions;
//using BanMod.Modules.CustomHats;
//using HarmonyLib;
//using Hazel;
//using InnerNet;
//using System;
//using System.Collections.Generic;
//using System.Linq;
//using UnityEngine;
//using static BanMod.Utils;

//namespace BanMod
//{
//    public enum CustomRPC : byte
//    {
//        ModdedHandshake = 212,
//        ProxySendChat = 213,
//        SetSpecialKiller = 214,
//        SetExiler = 215,
//        SetJester = 216,
//        SetWatcher = 222,
//        HostTripleBoolUpdate = 217,
//        RoleCommandAction = 218,
//        HandshakeModded = 219,
//        ModdedAllChat = 220,
//        HostRoleOptionsUpdate = 221,
//        SyncPlayerVisual = 223,
//        Sicko = 164,
//        KillNet = 154,
//        CREWMODIMPOSTORFORCE = 255
//    }

//    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))]
//    internal class UnifiedRPCHandlerPatch
//    {
//        private const string CurrentModVersion = BanMod.PluginVersion;
//        private const string LogTag = "RPCHandler";
//        private const int MaxRpcDataSize = 256;

//        private const int MaxTotalRpcPerWindow = 25;
//        private const float TotalRpcWindowSeconds = 0.35f;

//        private const int MaxSameRpcPerWindow = 15;
//        private const float SameRpcWindowSeconds = 0.15f;

//        private const int MaxPlayAnimationPerWindow = 10;
//        private const float PlayAnimationWindowSeconds = 0.40f;

//        private const int MaxChatMessagesPerWindow = 5;
//        private const float ChatWindowSeconds = 2.0f;

//        public static readonly Dictionary<byte, string> ModdedClients = new();
//        public static readonly HashSet<byte> AlreadyHandledCheaters = new();
//        private static readonly HashSet<byte> AlreadyNotifiedModdedClients = new();

//        private static readonly Dictionary<int, Queue<float>> GlobalRpcRateByClient = new();
//        private static readonly Dictionary<int, Dictionary<int, Queue<float>>> RpcRateByClientAndCall = new();
//        private static readonly Dictionary<int, Queue<float>> ChatRateByClient = new();

//        private static string NomeGiocatore(PlayerControl player)
//            => player?.Data?.PlayerName ?? player?.name ?? "Sconosciuto";

//        private static string EtichettaGiocatore(PlayerControl player)
//            => player == null ? "giocatore-nullo" : $"{NomeGiocatore(player)} [pid={player.PlayerId}]";

//        public static void ResetModdedNotifications()
//        {
//            AlreadyNotifiedModdedClients.Clear();
//        }

//        private static bool IsHostPlayer(PlayerControl player)
//        {
//            if (player == null || AmongUsClient.Instance == null)
//                return false;

//            try
//            {
//                if (player.OwnerId == AmongUsClient.Instance.HostId)
//                    return true;
//            }
//            catch
//            {
//            }

//            try
//            {
//                if (player.Data != null && player.Data.ClientId == AmongUsClient.Instance.HostId)
//                    return true;
//            }
//            catch
//            {
//            }

//            return false;
//        }

//        private static void LogSoloFile(string testo)
//        {
//            BMLogger.LogDebug(testo, "RPC-VERBOSE");
//        }

//        private static string PulisciTestoLog(string testo)
//        {
//            if (string.IsNullOrEmpty(testo))
//                return string.Empty;

//            return testo.Replace("\r", "\\r").Replace("\n", "\\n");
//        }

//        private static int GetConfiguredAction()
//        {
//            try
//            {
//                int action = Options.ActionSicko.GetValue();
//                return action == 1 ? 1 : 2;
//            }
//            catch
//            {
//                return 2;
//            }
//        }

//        private static string GetActionLabel(int action)
//            => action == 2 ? "bannato" : "espulso";

//        private static string GetRpcNome(int callId, bool hasByteId, byte callIdByte)
//        {
//            if (!hasByteId)
//                return $"RPC_INT_{callId}";

//            if (Enum.IsDefined(typeof(CustomRPC), callIdByte))
//                return $"CustomRPC.{(CustomRPC)callIdByte}";

//            if (Enum.IsDefined(typeof(RpcCalls), callIdByte))
//                return $"RpcCalls.{(RpcCalls)callIdByte}";

//            try
//            {
//                string mapped = RpcMap.GetRpcName(callIdByte);
//                if (!string.IsNullOrWhiteSpace(mapped))
//                    return mapped;
//            }
//            catch
//            {
//            }

//            return $"RPC_BYTE_{callIdByte}";
//        }

//        private static void RegistraRpcRicevuta(PlayerControl player, int callId, bool hasByteId, byte callIdByte, MessageReader reader)
//        {
//            string rpcName = GetRpcNome(callId, hasByteId, callIdByte);
//            int length = reader?.Length ?? -1;
//            int position = reader?.Position ?? -1;
//            int remaining = reader == null ? -1 : length - position;
//            int clientId = player != null ? player.GetClientId() : -1;

//            LogSoloFile(
//                $"RPC ricevuta | Giocatore={EtichettaGiocatore(player)} | ClientId={clientId} | " +
//                $"CallIdInt={callId} | CallIdByte={(hasByteId ? callIdByte.ToString() : "N/D")} | " +
//                $"Nome={rpcName} | Posizione={position} | Lunghezza={length} | Rimanenti={remaining}"
//            );
//        }

//        private static void RegistraMessaggioRicevuto(PlayerControl player, string tipo, string contenuto)
//        {
//            LogSoloFile(
//                $"Messaggio ricevuto | Tipo={tipo} | Giocatore={EtichettaGiocatore(player)} | " +
//                $"Testo={PulisciTestoLog(contenuto)}"
//            );
//        }

//        private static bool RegisterRateHit(
//            Dictionary<int, Queue<float>> store,
//            int key,
//            float now,
//            float windowSeconds,
//            int maxAllowed,
//            out int currentCount)
//        {
//            if (!store.TryGetValue(key, out var queue))
//            {
//                queue = new Queue<float>();
//                store[key] = queue;
//            }

//            while (queue.Count > 0 && now - queue.Peek() > windowSeconds)
//                queue.Dequeue();

//            queue.Enqueue(now);
//            currentCount = queue.Count;

//            return currentCount > maxAllowed;
//        }

//        private static bool RegisterPerRpcRateHit(
//            int clientId,
//            int callId,
//            float now,
//            float windowSeconds,
//            int maxAllowed,
//            out int currentCount)
//        {
//            if (!RpcRateByClientAndCall.TryGetValue(clientId, out var rpcMap))
//            {
//                rpcMap = new Dictionary<int, Queue<float>>();
//                RpcRateByClientAndCall[clientId] = rpcMap;
//            }

//            if (!rpcMap.TryGetValue(callId, out var queue))
//            {
//                queue = new Queue<float>();
//                rpcMap[callId] = queue;
//            }

//            while (queue.Count > 0 && now - queue.Peek() > windowSeconds)
//                queue.Dequeue();

//            queue.Enqueue(now);
//            currentCount = queue.Count;

//            return currentCount > maxAllowed;
//        }

//        private static void ShowScreenMessage(string message)
//        {
//            try
//            {
//                if (HudManager.Instance?.Notifier != null && !string.IsNullOrWhiteSpace(message))
//                    HudManager.Instance.Notifier.AddDisconnectMessage(message);
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Warn($"Impossibile mostrare la notifica a schermo: {ex.Message}", LogTag);
//            }
//        }

//        private static void ApplicaAzioneSospetto(
//            PlayerControl player,
//            ClientData client,
//            int cheaterClientId,
//            int action,
//            string motivoBan,
//            string messaggioSchermo,
//            bool aggiungiBan = true,
//            bool aggiungiCheater = true,
//            bool inviaWarning = true)
//        {
//            if (player == null)
//                return;

//            action = action == 1 ? 1 : 2;

//            byte playerId = player.PlayerId;
//            string nome = NomeGiocatore(player);
//            bool firstHandle = AlreadyHandledCheaters.Add(playerId);

//            BMLogger.Warn(
//                $"Azione anti-cheat applicata | Giocatore={nome} | ClientId={cheaterClientId} | Action={action} | Motivo={motivoBan}",
//                LogTag);

//            ShowScreenMessage(messaggioSchermo);

//            if (client != null && firstHandle)
//            {
//                try
//                {
//                    if (aggiungiCheater)
//                    {
//                        CheaterManager.AddPlayer(client);
//                        BMLogger.Info($"Giocatore aggiunto alla lista cheater: {nome} | ClientId={cheaterClientId}", LogTag);
//                    }
//                }
//                catch (Exception ex)
//                {
//                    BMLogger.Error($"Errore durante l'aggiunta ai cheater: {ex}", LogTag);
//                }

//                try
//                {
//                    if (aggiungiBan && action == 2)
//                    {
//                        BanManager.AddBanPlayer(client, motivoBan);
//                        BMLogger.Info($"Giocatore aggiunto alla ban list: {nome} | ClientId={cheaterClientId}", LogTag);
//                    }
//                }
//                catch (Exception ex)
//                {
//                    BMLogger.Error($"Errore durante l'aggiunta alla ban list: {ex}", LogTag);
//                }
//            }

//            if (firstHandle && inviaWarning)
//            {
//                try
//                {
//                    AntiCheat.SendGlobalHackWarning();
//                }
//                catch (Exception ex)
//                {
//                    BMLogger.Warn($"Impossibile inviare il warning globale anti-cheat: {ex.Message}", LogTag);
//                }
//            }

//            try
//            {
//                if (cheaterClientId >= 0 && AmongUsClient.Instance != null)
//                {
//                    AmongUsClient.Instance.KickPlayer(cheaterClientId, action == 2);
//                    BMLogger.Warn(
//                        $"{(action == 2 ? "Ban" : "Kick")} inviato al client | Giocatore={nome} | ClientId={cheaterClientId}",
//                        LogTag);
//                }
//                else
//                {
//                    BMLogger.Warn(
//                        $"Impossibile applicare il kick/ban: ClientId non valido ({cheaterClientId}) | Giocatore={nome}",
//                        LogTag);
//                }
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Error($"Errore durante l'esecuzione del kick/ban per {nome} | ClientId={cheaterClientId}: {ex}", LogTag);
//            }
//        }

//        private static bool IsRpcDataValid(MessageReader reader, int callId, bool isCustom, string playerName)
//        {
//            if (reader == null)
//            {
//                BMLogger.Warn($"RPC non valida: reader nullo | Giocatore={playerName} | CallId={callId}", LogTag);
//                return false;
//            }

//            if (reader.Position < 0 || reader.Position > reader.Length)
//            {
//                BMLogger.Warn(
//                    $"RPC corrotta rilevata | Giocatore={playerName} | CallId={callId} | Position={reader.Position} | Length={reader.Length}",
//                    LogTag);
//                return false;
//            }

//            if (reader.Length > MaxRpcDataSize && !isCustom)
//            {
//                BMLogger.Error(
//                    $"RPC bloccata per dimensione eccessiva | Giocatore={playerName} | CallId={callId} | Dimensione={reader.Length} | Limite={MaxRpcDataSize}",
//                    LogTag);
//                return false;
//            }

//            return true;
//        }

//        private static bool IsChatDangerous(string text, bool allowLongModdedText = false)
//        {
//            if (string.IsNullOrEmpty(text))
//                return false;

//            if (!allowLongModdedText && text.Length > 200)
//                return true;

//            if (allowLongModdedText && text.Length > 4000)
//                return true;

//            string lowerText = text.ToLowerInvariant();

//            if (lowerText.Contains("<size") ||
//                lowerText.Contains("voffset") ||
//                lowerText.Contains("<mark") ||
//                lowerText.Contains("<material") ||
//                lowerText.Contains("<quad"))
//            {
//                return true;
//            }

//            int spriteCount = lowerText.Split(new[] { "<sprite" }, StringSplitOptions.None).Length - 1;
//            if (spriteCount > 3)
//                return true;

//            return false;
//        }

//        private static bool HandleLegacyExploitRpc(
//            PlayerControl player,
//            ClientData client,
//            int cheaterClientId,
//            int action,
//            int callId,
//            string playerName)
//        {
//            switch (callId)
//            {
//                case 101:
//                    BMLogger.Warn($"Rilevata RPC legacy sospetta 101 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "Legacy_Mod_Detected_101",
//                        $"{playerName} {GetActionLabel(action)} per mod legacy/HostGuard (RPC 101).");
//                    return true;

//                case 150:
//                    BMLogger.Warn($"Rilevata RPC sospetta 150 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "BetterAmongUs_RPC_150",
//                        $"{playerName} {GetActionLabel(action)} per BetterAmongUs (RPC 150).");
//                    return true;

//                case 176:
//                    BMLogger.Warn($"Rilevata RPC sospetta 176 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "HostGuard_RPC_176",
//                        $"{playerName} {GetActionLabel(action)} per HostGuard (RPC 176).");
//                    return true;

//                case 250:
//                    BMLogger.Warn($"Rilevata RPC sospetta 250 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "KillNetwork_RPC_250",
//                        $"{playerName} {GetActionLabel(action)} per KillNetwork (RPC 250).");
//                    return true;

//                case 420:
//                    BMLogger.Warn($"Rilevata RPC sospetta 420 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "SickoMenu_RPC_420",
//                        $"{playerName} {GetActionLabel(action)} per SickoMenu (RPC 420).");
//                    return true;

//                case 666:
//                    BMLogger.Warn($"Rilevata RPC sospetta 666 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "GoatNetClient_RPC_666",
//                        $"{playerName} {GetActionLabel(action)} per GoatNetClient (RPC 666).");
//                    return true;

//                case 42069:
//                    BMLogger.Warn($"Rilevata RPC sospetta 42069 da {playerName}.", LogTag);
//                    ApplicaAzioneSospetto(
//                        player, client, cheaterClientId, action,
//                        "AmongUsMenu_RPC_42069",
//                        $"{playerName} {GetActionLabel(action)} per AmongUsMenu (RPC 42069).");
//                    return true;
//            }

//            return false;
//        }
//        private static string NormalizeModVersion(string modInfo)
//        {
//            if (string.IsNullOrWhiteSpace(modInfo))
//                return "";

//            string clean = modInfo.Trim();

//            if (clean.Contains("|"))
//            {
//                string[] parts = clean.Split('|');
//                if (parts.Length >= 2)
//                    return parts[1].Trim();
//            }

//            string[] tokens = clean.Split(' ');
//            foreach (string token in tokens)
//            {
//                string t = token.Trim();
//                if (System.Text.RegularExpressions.Regex.IsMatch(t, @"^\d+\.\d+(\.\d+)?"))
//                    return t;
//            }

//            return "";
//        }

//        private static bool IsSameModVersion(string remoteModInfo)
//        {
//            string remoteVersion = NormalizeModVersion(remoteModInfo);

//            if (string.IsNullOrWhiteSpace(remoteVersion))
//                return false;

//            return string.Equals(
//                remoteVersion,
//                CurrentModVersion,
//                StringComparison.OrdinalIgnoreCase
//            );
//        }

//        private static void BanForWrongModVersion(PlayerControl player, ClientData client, string remoteModInfo)
//        {
//            if (!AmongUsClient.Instance.AmHost)
//                return;

//            if (player == null)
//                return;

//            int clientId = player.GetClientId();
//            string playerName = NomeGiocatore(player);
//            string remoteVersion = NormalizeModVersion(remoteModInfo);

//            string reason = string.IsNullOrWhiteSpace(remoteVersion)
//                ? "Wrong_Mod_Version_Missing"
//                : $"Wrong_Mod_Version_{remoteVersion}_Expected_{CurrentModVersion}";

//            BMLogger.Warn(
//                $"Versione mod diversa rilevata | Player={playerName} | ClientId={clientId} | RemoteInfo={remoteModInfo} | RemoteVersion={remoteVersion} | Expected={CurrentModVersion}",
//                LogTag
//            );

//            try
//            {
//                ShowScreenMessage($"{playerName} bannato: versione BANMOD diversa.");
//            }
//            catch
//            {
//            }

//            try
//            {
//                if (clientId >= 0)
//                {
//                    AmongUsClient.Instance.KickPlayer(clientId, false);
//                    BMLogger.Warn($"Ban inviato per versione mod diversa | Player={playerName} | ClientId={clientId}", LogTag);
//                }
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Error($"Errore ban versione diversa | Player={playerName} | ClientId={clientId} | {ex}", LogTag);
//            }
//        }
//        private static void DisconnectForWrongHostModVersion(string hostModInfo)
//        {
//            try
//            {
//                if (AmongUsClient.Instance == null)
//                    return;

//                string hostVersion = NormalizeModVersion(hostModInfo);

//                BMLogger.Warn(
//                    $"Host con versione BANMOD incompatibile | HostInfo={hostModInfo} | HostVersion={hostVersion} | Expected={CurrentModVersion}",
//                    LogTag
//                );

//                try
//                {
//                    ShowScreenMessage(
//                        $"Lobby incompatibile: host BANMOD {hostVersion}, tua versione {CurrentModVersion}."
//                    );
//                }
//                catch
//                {
//                }

//                AmongUsClient.Instance.ExitGame(DisconnectReasons.Custom);
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Error($"Errore durante disconnessione per versione host diversa: {ex}", LogTag);
//            }
//        }
//        public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] int callId, [HarmonyArgument(1)] MessageReader reader)
//        {
//            if (__instance == null)
//            {
//                BMLogger.Warn("HandleRpc intercettata con PlayerControl nullo.", LogTag);
//                return true;
//            }

//            if (reader == null)
//            {
//                BMLogger.Warn($"HandleRpc intercettata con reader nullo | Giocatore={EtichettaGiocatore(__instance)} | CallIdInt={callId}", LogTag);
//                return false;
//            }

//            bool hasByteId = callId >= byte.MinValue && callId <= byte.MaxValue;
//            byte callIdByte = hasByteId ? (byte)callId : byte.MaxValue;

//            RegistraRpcRicevuta(__instance, callId, hasByteId, callIdByte, reader);

//            if (PlayerControl.LocalPlayer == __instance)
//                return true;

//            string name = NomeGiocatore(__instance);
//            int cheaterClientId = __instance.GetClientId();
//            ClientData client = ExtendedPlayerControl.GetClient(__instance);
//            int action = GetConfiguredAction();
//            float now = Time.realtimeSinceStartup;

//            int originalReaderPosition = reader.Position;

//            try
//            {
//                if (HandleLegacyExploitRpc(__instance, client, cheaterClientId, action, callId, name))
//                    return false;

//                if (!hasByteId)
//                {
//                    BMLogger.Warn(
//                        $"RPC con CallId fuori range byte rilevata | Giocatore={name} | CallIdInt={callId}",
//                        LogTag);

//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "RPC_CallId_Fuori_Range",
//                        $"{name} {GetActionLabel(action)} per RPC con identificatore non valido."
//                    );

//                    return false;
//                }

//                bool isCustom = Enum.IsDefined(typeof(CustomRPC), callIdByte);
//                bool isStandard = Enum.IsDefined(typeof(RpcCalls), callIdByte);

//                if (!IsRpcDataValid(reader, callId, isCustom, name))
//                {
//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "Suspect_Corrupted_RPC",
//                        $"{name} {GetActionLabel(action)} per RPC malformata.",
//                        aggiungiBan: true,
//                        aggiungiCheater: true,
//                        inviaWarning: true
//                    );

//                    return false;
//                }

//                if (cheaterClientId >= 0 &&
//                    RegisterRateHit(
//                        GlobalRpcRateByClient,
//                        cheaterClientId,
//                        now,
//                        TotalRpcWindowSeconds,
//                        MaxTotalRpcPerWindow,
//                        out int totalRpcCount))
//                {
//                    BMLogger.Warn(
//                        $"Flood totale RPC rilevato | Giocatore={name} | ClientId={cheaterClientId} | " +
//                        $"Conteggio={totalRpcCount} in {TotalRpcWindowSeconds:0.00}s",
//                        LogTag);

//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "Global_RPC_Flood_Window",
//                        $"{name} {GetActionLabel(action)} per flood RPC massivo."
//                    );

//                    return false;
//                }

//                if (cheaterClientId >= 0)
//                {
//                    int sameRpcLimit = callIdByte == (byte)RpcCalls.PlayAnimation
//                        ? MaxPlayAnimationPerWindow
//                        : MaxSameRpcPerWindow;

//                    float sameRpcWindow = callIdByte == (byte)RpcCalls.PlayAnimation
//                        ? PlayAnimationWindowSeconds
//                        : SameRpcWindowSeconds;

//                    bool sameRpcFlood = RegisterPerRpcRateHit(
//                        cheaterClientId,
//                        callId,
//                        now,
//                        sameRpcWindow,
//                        sameRpcLimit,
//                        out int sameRpcCount);

//                    LogSoloFile(
//                        $"Storico RPC | Giocatore={EtichettaGiocatore(__instance)} | CallIdInt={callId} | " +
//                        $"ConteggioFinestra={sameRpcCount} | Finestra={sameRpcWindow:0.00}s"
//                    );

//                    if (sameRpcFlood)
//                    {
//                        string reasonCode = callIdByte == (byte)RpcCalls.PlayAnimation
//                            ? "RPC_PlayAnimation_Abuse"
//                            : "Suspect_Cheater_RPCFlood";

//                        string reasonText = callIdByte == (byte)RpcCalls.PlayAnimation
//                            ? "abuso di PlayAnimation"
//                            : $"flood RPC {GetRpcNome(callId, true, callIdByte)}";

//                        BMLogger.Warn(
//                            $"Flood RPC specifica rilevato | Giocatore={name} | CallId={callId} | Conteggio={sameRpcCount}",
//                            LogTag);

//                        ApplicaAzioneSospetto(
//                            __instance,
//                            client,
//                            cheaterClientId,
//                            action,
//                            reasonCode,
//                            $"{name} {GetActionLabel(action)} per {reasonText}.",
//                            aggiungiBan: true,
//                            aggiungiCheater: true,
//                            inviaWarning: true
//                        );

//                        return false;
//                    }
//                }

//                if (callIdByte != (byte)CustomRPC.ModdedAllChat &&
//                    callIdByte != (byte)CustomRPC.ProxySendChat &&
//                    callIdByte != (byte)CustomRPC.RoleCommandAction &&
//                    callIdByte != (byte)CustomRPC.SetSpecialKiller &&
//                    callIdByte != (byte)CustomRPC.SetExiler &&
//                    callIdByte != (byte)CustomRPC.SetJester &&
//                    callIdByte != (byte)CustomRPC.SetWatcher &&
//                    callIdByte != (byte)CustomRPC.SyncPlayerVisual &&
//                    callIdByte != (byte)CustomRPC.HostTripleBoolUpdate &&
//                    callIdByte != (byte)CustomRPC.HostRoleOptionsUpdate &&
//                    callIdByte != (byte)CustomRPC.HandshakeModded &&
//                    callIdByte != (byte)CustomHatSync.RpcId &&
//                    callIdByte != (byte)CustomRPC.ModdedHandshake)
//                {
//                    try
//                    {
//                        reader.Position = originalReaderPosition;
//                        AntiCheat.PlayerControlReceiveRpc(__instance, callIdByte, reader);
//                    }
//                    finally
//                    {
//                        reader.Position = originalReaderPosition;
//                    }
//                }

//                MessageReader subReader = MessageReader.Get(reader);

//                LogSoloFile(
//                    $"Dettaglio RPC | Giocatore={EtichettaGiocatore(__instance)} | NomeRpc={GetRpcNome(callId, true, callIdByte)}"
//                );

//                if (AmongUsClient.Instance.AmHost &&
//                    callIdByte == (byte)RpcCalls.Exiled &&
//                    MeetingHud.Instance == null &&
//                    !__instance.AmOwner)
//                {
//                    BMLogger.Warn($"Bloccata RPC Exiled fuori meeting da {name}.", LogTag);

//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "Crash_RPC_Exiled",
//                        $"{name} {GetActionLabel(action)} per RPC Exiled sospetta."
//                    );

//                    return false;
//                }

//                if (AmongUsClient.Instance.AmHost &&
//                    callIdByte == (byte)RpcCalls.VotingComplete &&
//                    GameStates.isLobby &&
//                    MeetingHud.Instance == null)
//                {
//                    BMLogger.Warn($"Bloccata RPC VotingComplete in lobby da {name}.", LogTag);

//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "Crash_VotingComplete_Lobby",
//                        $"{name} {GetActionLabel(action)} per VotingComplete sospetta in lobby."
//                    );

//                    return false;
//                }

//                if (callIdByte == CustomHatSync.RpcId)
//                {
//                    try
//                    {
//                        CustomHatSync.ReceiveRpc(subReader);
//                    }
//                    catch (Exception ex)
//                    {
//                        BMLogger.Warn($"CustomHatSync RPC 240 non valida da {name}: {ex.Message}", LogTag);
//                    }

//                    return false;
//                }

//                if (!isCustom && !isStandard)
//                {
//                    LogSoloFile(
//                        $"RPC sconosciuta ricevuta | Giocatore={EtichettaGiocatore(__instance)} | CallIdInt={callId} | CallIdByte={callIdByte}"
//                    );

//                    ApplicaAzioneSospetto(
//                        __instance,
//                        client,
//                        cheaterClientId,
//                        action,
//                        "RPC_Sconosciuta",
//                        $"{name} {GetActionLabel(action)} per RPC sconosciuta ({callIdByte})."
//                    );

//                    return false;
//                }

//                if (isCustom)
//                {
//                    var customType = (CustomRPC)callIdByte;
//                    LogSoloFile($"Gestione CustomRPC | Giocatore={EtichettaGiocatore(__instance)} | Tipo={customType}");

//                    switch (customType)
//                    {
//                        case CustomRPC.HandshakeModded:
//                            {
//                                RegisterModdedClient(__instance.PlayerId, "BanMod");

//                                LogSoloFile($"HandshakeModded registrato per {EtichettaGiocatore(__instance)}.");
//                                return false;
//                            }

//                        case CustomRPC.ModdedHandshake:
//                            {
//                                string modInfo = subReader.ReadString();
//                                bool isRemoteModded = subReader.ReadBoolean();
//                                byte playerId = __instance.PlayerId;

//                                string cleanModInfo = string.IsNullOrWhiteSpace(modInfo) ? "BanMod" : modInfo;

//                                if (isRemoteModded)
//                                {
//                                    RegisterModdedClient(playerId, cleanModInfo);
//                                }

//                                LogSoloFile(
//                                    $"ModdedHandshake ricevuta | Giocatore={EtichettaGiocatore(__instance)} | ModInfo={PulisciTestoLog(cleanModInfo)} | FlagModded={isRemoteModded}"
//                                );

//                                if (AmongUsClient.Instance != null &&
//                                    AmongUsClient.Instance.AmHost &&
//                                    isRemoteModded &&
//                                    !IsHostPlayer(__instance))
//                                {
//                                    if (!IsSameModVersion(cleanModInfo))
//                                    {
//                                        BanForWrongModVersion(__instance, client, cleanModInfo);
//                                        return false;
//                                    }
//                                }

//                                if (AmongUsClient.Instance != null &&
//                                    !AmongUsClient.Instance.AmHost &&
//                                    isRemoteModded &&
//                                    IsHostPlayer(__instance))
//                                {
//                                    if (!IsSameModVersion(cleanModInfo))
//                                    {
//                                        DisconnectForWrongHostModVersion(cleanModInfo);
//                                        return false;
//                                    }
//                                }

//                                if (isRemoteModded &&
//                                    AlreadyNotifiedModdedClients.Add(playerId) &&
//                                    HudManager.Instance?.Notifier != null)
//                                {
//                                    HudManager.Instance.Notifier.AddDisconnectMessage(
//                                        $"Client moddato connesso: {NomeGiocatore(__instance)} | Mod: {cleanModInfo}"
//                                    );
//                                }

//                                return false;
//                            }

//                        case CustomRPC.HostTripleBoolUpdate:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"HostTripleBoolUpdate BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                bool remoteBool1ImmortalAdded = subReader.ReadBoolean();
//                                bool remoteBool2ImmortalEnabled = subReader.ReadBoolean();
//                                bool remoteBool3EngineerEnabled = subReader.ReadBoolean();

//                                HostOptionStatus.UpdateHostRules(
//                                    remoteBool1ImmortalAdded,
//                                    remoteBool2ImmortalEnabled,
//                                    remoteBool3EngineerEnabled
//                                );

//                                LogSoloFile(
//                                    $"HostTripleBoolUpdate ricevuta | Added={remoteBool1ImmortalAdded} | Enabled={remoteBool2ImmortalEnabled} | Engineer={remoteBool3EngineerEnabled}"
//                                );

//                                return false;
//                            }

//                        case CustomRPC.ProxySendChat:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"ProxySendChat BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                string message = subReader.ReadString();
//                                string titoloIgnorato = subReader.ReadString();
//                                byte target = subReader.ReadByte();

//                                RegistraMessaggioRicevuto(__instance, "ProxySendChat", message);

//                                LogSoloFile(
//                                    $"ProxySendChat ricevuto | TitoloIgnorato={PulisciTestoLog(titoloIgnorato)} | TargetPlayerId={target}");

//                                PlayerControl localPlayer = PlayerControl.LocalPlayer;

//                                if (localPlayer == null || localPlayer.Data == null)
//                                    return false;

//                                if (localPlayer.Data.IsDead || localPlayer.Data.Disconnected)
//                                    return false;

//                                if (string.IsNullOrWhiteSpace(message))
//                                    return false;

//                                message = message
//                                    .RemoveHtmlTags()
//                                    .Replace("\\r\\n", "\n")
//                                    .Replace("\\n", "\n")
//                                    .Replace("\\r", "\n")
//                                    .Replace("\r\n", "\n")
//                                    .Replace("\r", "\n")
//                                    .Trim();

//                                if (string.IsNullOrWhiteSpace(message))
//                                    return false;

//                                if (message.Length > 120)
//                                    message = message.Substring(0, 120);

//                                bool sendToAll = target == byte.MaxValue || target == 255;
//                                bool targetIsProxySelf = target == localPlayer.PlayerId;

//                                int targetClientId = -1;

//                                if (!sendToAll && !targetIsProxySelf)
//                                {
//                                    PlayerControl targetPlayer = Utils.GetPlayerById(target);

//                                    if (targetPlayer == null || targetPlayer.Data == null || targetPlayer.Data.Disconnected)
//                                        return false;

//                                    targetClientId = targetPlayer.GetClientId();

//                                    if (targetClientId < 0)
//                                        return false;
//                                }

//                                if (targetIsProxySelf)
//                                {
//                                    try
//                                    {
//                                        DestroyableSingleton<HudManager>.Instance.Chat.AddChat(localPlayer, message, false);
//                                        MessageBlocker.UpdateLastMessageTime();

//                                        LogSoloFile(
//                                            $"ProxySendChat mostrato localmente al proxy stesso | Proxy={EtichettaGiocatore(localPlayer)} | TargetPlayerId={target} | Testo={PulisciTestoLog(message)}");
//                                    }
//                                    catch (Exception ex)
//                                    {
//                                        BMLogger.Warn($"ProxySendChat AddChat locale fallito: {ex}", LogTag);
//                                    }

//                                    return false;
//                                }

//                                if (!MessageBlocker.CanSendMessage())
//                                {
//                                    ProxyMessageQueue.Enqueue(message, targetClientId, sendToAll);

//                                    LogSoloFile(
//                                        $"ProxySendChat accodata per cooldown | TargetPlayerId={target} | TargetClientId={targetClientId} | SendToAll={sendToAll} | Testo={PulisciTestoLog(message)}");

//                                    return false;
//                                }

//                                try
//                                {
//                                    var writer = CustomRpcSender.Create("ProxySendChatDirect", SendOption.Reliable);

//                                    writer.StartMessage(targetClientId);
//                                    writer.StartRpc(localPlayer.NetId, (byte)RpcCalls.SendChat)
//                                        .Write(message)
//                                        .EndRpc();
//                                    writer.EndMessage();
//                                    writer.SendMessage();

//                                    if (sendToAll)
//                                    {
//                                        try
//                                        {
//                                            DestroyableSingleton<HudManager>.Instance.Chat.AddChat(localPlayer, message, false);
//                                        }
//                                        catch (Exception ex)
//                                        {
//                                            BMLogger.Warn($"ProxySendChat AddChat broadcast locale fallito: {ex}", LogTag);
//                                        }
//                                    }

//                                    MessageBlocker.UpdateLastMessageTime();

//                                    LogSoloFile(
//                                        $"ProxySendChat inviato | Proxy={EtichettaGiocatore(localPlayer)} | TargetPlayerId={target} | TargetClientId={targetClientId} | SendToAll={sendToAll} | Testo={PulisciTestoLog(message)}");
//                                }
//                                catch (Exception ex)
//                                {
//                                    BMLogger.Warn($"ProxySendChat invio fallito: {ex}", LogTag);
//                                }

//                                return false;
//                            }
//                        case CustomRPC.SyncPlayerVisual:
//                            {
//                                PlayerMouseController.ReceiveSyncPlayerVisual(subReader, cheaterClientId);
//                                return false;
//                            }
//                        case CustomRPC.ModdedAllChat:
//                            {
//                                byte senderId = subReader.ReadByte();
//                                string text = subReader.ReadString();

//                                RegistraMessaggioRicevuto(__instance, "ModdedAllChat", text);

//                                if (IsChatDangerous(text, true))
//                                {
//                                    BMLogger.Warn(
//                                        $"Messaggio ModdedAllChat pericoloso bloccato da {name}: {PulisciTestoLog(text)}",
//                                        LogTag);

//                                    return false;
//                                }

//                                if (senderId != __instance.PlayerId)
//                                {
//                                    BMLogger.Warn(
//                                        $"ModdedAllChat senderId mismatch | RPC owner={__instance.PlayerId} | senderId={senderId}",
//                                        LogTag);

//                                    return false;
//                                }

//                                ModdedOriginalChatManager.ReceiveAll(senderId, text);

//                                try
//                                {
//                                    if (HudManager.Instance != null &&
//                                        HudManager.Instance.Chat != null &&
//                                        !HudManager.Instance.Chat.IsOpenOrOpening)
//                                    {
//                                        ChatControllerUpdatePatch.TryFlashChatNotifyDot();
//                                    }
//                                }
//                                catch
//                                {
//                                }

//                                return false;
//                            }
//                        case CustomRPC.HostRoleOptionsUpdate:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"HostRoleOptionsUpdate BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                HostRoleOptionsRpc.Receive(__instance, subReader);
//                                return false;
//                            }

//                        case CustomRPC.RoleCommandAction:
//                            {
//                                RoleCommandActionRpc.Receive(__instance, subReader);
//                                return false;
//                            }

//                        case CustomRPC.SetSpecialKiller:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"SetSpecialKiller BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                byte killerId = subReader.ReadByte();

//                                Guesser.SpecialKillerId = killerId;
//                                Guesser.SpecialKillerSelected = killerId != byte.MaxValue && killerId != 255;

//                                LogSoloFile($"SetSpecialKiller ricevuta | KillerId={killerId}");

//                                try
//                                {
//                                    RoleButtonRefresh.RefreshNow();
//                                }
//                                catch
//                                {
//                                }

//                                return false;
//                            }

//                        case CustomRPC.SetJester:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"SetJester BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                byte jesterId = subReader.ReadByte();

//                                Jester.JesterId = jesterId;
//                                Jester.JesterSelected = jesterId != byte.MaxValue && jesterId != 255;

//                                try
//                                {
//                                    Jester.ForcedJesterId = jesterId;
//                                    Jester.ForcedJesterSelected = jesterId != byte.MaxValue && jesterId != 255;
//                                }
//                                catch
//                                {
//                                }

//                                LogSoloFile($"SetJester ricevuta | JesterId={jesterId}");

//                                try
//                                {
//                                    RoleButtonRefresh.RefreshNow();
//                                }
//                                catch
//                                {
//                                }

//                                return false;
//                            }

//                        case CustomRPC.SetExiler:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"SetExiler BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                byte exilerId = subReader.ReadByte();

//                                Exiler.ExilerId = exilerId;
//                                Exiler.ExilerSelected = exilerId != byte.MaxValue && exilerId != 255;

//                                LogSoloFile($"SetExiler ricevuta | ExilerId={exilerId}");

//                                try
//                                {
//                                    RoleButtonRefresh.RefreshNow();
//                                }
//                                catch
//                                {
//                                }

//                                return false;
//                            }
//                        case CustomRPC.SetWatcher:
//                            {
//                                PlayerControl sender = __instance;

//                                if (!IsHostPlayer(sender))
//                                {
//                                    LogSoloFile(
//                                        $"SetWatcher BLOCCATO: mittente non host | Sender={EtichettaGiocatore(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

//                                    return false;
//                                }
//                                byte watcherId = subReader.ReadByte();

//                                Watcher.WatcherId = watcherId;
//                                Watcher.WatcherSelected = watcherId != byte.MaxValue && watcherId != 255;

//                                LogSoloFile($"SetWatcher ricevuta | WatcherId={watcherId}");

//                                return false;
//                            }
//                        case CustomRPC.Sicko:
//                            {
//                                ApplicaAzioneSospetto(
//                                    __instance,
//                                    client,
//                                    cheaterClientId,
//                                    action,
//                                    "Suspect_Cheater_SickoMenu",
//                                    $"{name} {GetActionLabel(action)} per SickoMenu."
//                                );

//                                return false;
//                            }

//                        case CustomRPC.CREWMODIMPOSTORFORCE:
//                            {
//                                ApplicaAzioneSospetto(
//                                    __instance,
//                                    client,
//                                    cheaterClientId,
//                                    action,
//                                    "Suspect_CREWMODIMPOSTORFORCE",
//                                    $"{name} {GetActionLabel(action)} per CREWMODIMPOSTORFORCE."
//                                );

//                                return false;
//                            }

//                        case CustomRPC.KillNet:
//                            {
//                                ApplicaAzioneSospetto(
//                                    __instance,
//                                    client,
//                                    cheaterClientId,
//                                    action,
//                                    "Suspect_Cheater_KillNet",
//                                    $"{name} {GetActionLabel(action)} per KillNetWork Hack."
//                                );

//                                return false;
//                            }
//                    }

//                    return false;
//                }

//                var rpcType = (RpcCalls)callIdByte;
//                LogSoloFile($"Gestione RpcCalls standard | Giocatore={EtichettaGiocatore(__instance)} | Tipo={rpcType}");

//                switch (rpcType)
//                {
//                    case RpcCalls.SendChat:
//                        {
//                            string text = subReader.ReadString();
//                            RegistraMessaggioRicevuto(__instance, "SendChat", text);

//                            if (cheaterClientId >= 0 &&
//                                RegisterRateHit(
//                                    ChatRateByClient,
//                                    cheaterClientId,
//                                    now,
//                                    ChatWindowSeconds,
//                                    MaxChatMessagesPerWindow,
//                                    out int chatCount))
//                            {
//                                BMLogger.Warn(
//                                    $"Chat flood rilevato | Giocatore={name} | ClientId={cheaterClientId} | Conteggio={chatCount} in {ChatWindowSeconds:0.0}s",
//                                    LogTag);

//                                ApplicaAzioneSospetto(
//                                    __instance,
//                                    client,
//                                    cheaterClientId,
//                                    action,
//                                    "Chat_Flood",
//                                    $"{name} {GetActionLabel(action)} per chat flood."
//                                );

//                                return false;
//                            }

//                            if (IsChatDangerous(text))
//                            {
//                                BMLogger.Warn($"Messaggio chat pericoloso bloccato da {name}: {PulisciTestoLog(text)}", LogTag);

//                                ApplicaAzioneSospetto(
//                                    __instance,
//                                    client,
//                                    cheaterClientId,
//                                    action,
//                                    "Crash_Chat_Formatting",
//                                    $"{name} {GetActionLabel(action)} per messaggio chat pericoloso."
//                                );

//                                return false;
//                            }

//                            ChatCommands.OnReceiveChat(__instance, text, out var canceled);
//                            if (canceled)
//                            {
//                                LogSoloFile($"Messaggio chat annullato dai comandi | Giocatore={EtichettaGiocatore(__instance)}");
//                                return false;
//                            }

//                            break;
//                        }

//                    case RpcCalls.SendQuickChat:
//                        {
//                            RegistraMessaggioRicevuto(__instance, "SendQuickChat", "Quick chat");
//                            ChatCommands.OnReceiveChat(__instance, "Quick chat", out var canceledQuick);

//                            if (canceledQuick)
//                            {
//                                LogSoloFile($"Quick chat annullata dai comandi | Giocatore={EtichettaGiocatore(__instance)}");
//                                return false;
//                            }

//                            break;
//                        }

//                    default:
//                        break;
//                }

//                return true;
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Error(
//                    $"Errore durante la gestione RPC | Giocatore={name} | CallId={callId} | Errore={ex}",
//                    LogTag);

//                ApplicaAzioneSospetto(
//                    __instance,
//                    client,
//                    cheaterClientId,
//                    action,
//                    "RPC_Exception",
//                    $"{name} {GetActionLabel(action)} per RPC malformata o crash attempt."
//                );

//                return false;
//            }
//            finally
//            {
//                try
//                {
//                    reader.Position = originalReaderPosition;
//                }
//                catch (Exception ex)
//                {
//                    BMLogger.Warn($"Impossibile ripristinare la posizione del reader: {ex.Message}", LogTag);
//                }
//            }
//        }

//        public static void RegisterModdedClient(byte playerId, string modInfo = "BanMod")
//        {
//            try
//            {
//                if (!ModdedRegistry.ModdedPlayers.Contains(playerId))
//                    ModdedRegistry.ModdedPlayers.Add(playerId);
//            }
//            catch
//            {
//            }

//            if (string.IsNullOrWhiteSpace(modInfo))
//                modInfo = "BanMod";

//            ModdedClients[playerId] = modInfo;
//        }

//        public static void RegisterLocalAsModded()
//        {
//            try
//            {
//                if (PlayerControl.LocalPlayer == null)
//                    return;

//                string modInfo = AmongUsClient.Instance != null && AmongUsClient.Instance.AmHost
//                    ? "BanMod Host"
//                    : "BanMod";

//                RegisterModdedClient(PlayerControl.LocalPlayer.PlayerId, modInfo);
//            }
//            catch (Exception)
//            {

//            }
//        }

//        public static void SendLocalModdedHandshake()
//        {
//            try
//            {
//                if (AmongUsClient.Instance == null)
//                    return;

//                if (PlayerControl.LocalPlayer == null)
//                    return;

//                if (PlayerControl.LocalPlayer.NetId == 0)
//                    return;

//                RegisterLocalAsModded();

//                string modInfo = AmongUsClient.Instance.AmHost
//                    ? $"BANMOD|{CurrentModVersion}|HOST"
//                    : $"BANMOD|{CurrentModVersion}";

//                var writer = AmongUsClient.Instance.StartRpcImmediately(
//                    PlayerControl.LocalPlayer.NetId,
//                    (byte)CustomRPC.ModdedHandshake,
//                    SendOption.Reliable,
//                    -1);

//                writer.Write(modInfo);
//                writer.Write(true);

//                AmongUsClient.Instance.FinishRpcImmediately(writer);
//            }
//            catch
//            {
//            }
//        }

//        public static bool IsClientModded(byte playerId)
//        {
//            if (ModdedClients.ContainsKey(playerId))
//                return true;

//            try
//            {
//                if (ModdedRegistry.ModdedPlayers.Contains(playerId))
//                    return true;
//            }
//            catch
//            {
//            }

//            return false;
//        }

//        public static List<(PlayerControl player, string modInfo)> GetModdedPlayersWithInfo()
//        {
//            return PlayerControl.AllPlayerControls
//                .ToArray()
//                .Where(p => p != null && IsClientModded(p.PlayerId))
//                .Select(p =>
//                {
//                    string modInfo = "BanMod";

//                    if (ModdedClients.TryGetValue(p.PlayerId, out string savedInfo) &&
//                        !string.IsNullOrWhiteSpace(savedInfo))
//                    {
//                        modInfo = savedInfo;
//                    }

//                    return (p, modInfo);
//                })
//                .ToList();
//        }
//    }

//    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Start))]
//    internal static class PlayerControl_Start_ModdedHandshakePatch
//    {
//        private static bool SentHandshake;

//        public static void Postfix(PlayerControl __instance)
//        {
//            try
//            {
//                if (__instance == null)
//                    return;

//                if (PlayerControl.LocalPlayer == null)
//                    return;

//                if (__instance != PlayerControl.LocalPlayer)
//                    return;

//                if (SentHandshake)
//                    return;

//                SentHandshake = true;

//                ModdedHandshakeAutoSender.ResetHandshakeBurst();
//                ModdedHandshakeAutoSender.TrySendHandshakeNow(true);
//            }
//            catch (Exception)
//            {

//            }
//        }

//        public static void Reset()
//        {
//            SentHandshake = false;
//        }
//    }

//    internal static class ModdedHandshakeAutoSender
//    {
//        private const float HandshakeIntervalSeconds = 3f;
//        private const int MaxHandshakeBurstSends = 8;

//        private static float _nextHandshakeTime = 0f;
//        private static int _handshakeSends = 0;

//        public static void ResetHandshakeBurst()
//        {
//            _nextHandshakeTime = 0f;
//            _handshakeSends = 0;
//        }

//        public static void TrySendHandshakeNow(bool force = false)
//        {
//            try
//            {
//                if (AmongUsClient.Instance == null)
//                    return;

//                if (PlayerControl.LocalPlayer == null)
//                    return;

//                if (!force)
//                {
//                    if (_handshakeSends >= MaxHandshakeBurstSends)
//                        return;

//                    if (Time.realtimeSinceStartup < _nextHandshakeTime)
//                        return;
//                }

//                _nextHandshakeTime = Time.realtimeSinceStartup + HandshakeIntervalSeconds;
//                _handshakeSends++;

//                UnifiedRPCHandlerPatch.SendLocalModdedHandshake();
//            }
//            catch (Exception ex)
//            {
//                BMLogger.Warn($"[Handshake] TrySendHandshakeNow failed: {ex}", "RPCHandler");
//            }
//        }
//    }

//    [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.Update))]
//    internal static class AmongUsClient_Update_ModdedHandshakePatch
//    {
//        public static void Postfix()
//        {
//            try
//            {
//                if (AmongUsClient.Instance == null)
//                    return;

//                if (PlayerControl.LocalPlayer == null)
//                    return;

//                if (!GameStates.isOnlineGame)
//                    return;

//                ModdedHandshakeAutoSender.TrySendHandshakeNow(false);
//            }
//            catch
//            {
//            }
//        }
//    }

//    [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameJoined))]
//    internal static class AmongUsClient_OnGameJoined_ModdedHandshakePatch
//    {
//        public static void Postfix()
//        {
//            try
//            {
//                UnifiedRPCHandlerPatch.ModdedClients.Clear();

//                try
//                {
//                    ModdedRegistry.ModdedPlayers.Clear();
//                    UnifiedRPCHandlerPatch.ResetModdedNotifications();
//                    HostRoleOptionsStatus.Reset();
//                }
//                catch
//                {
//                }

//                PlayerControl_Start_ModdedHandshakePatch.Reset();

//                UnifiedRPCHandlerPatch.RegisterLocalAsModded();

//                ModdedHandshakeAutoSender.ResetHandshakeBurst();
//                ModdedHandshakeAutoSender.TrySendHandshakeNow(true);
//            }
//            catch (Exception)
//            {

//            }
//        }
//    }


//    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Exiled))]
//    internal class ExiledLobbyGuardPatch
//    {
//        static bool Prefix(PlayerControl __instance)
//        {
//            if (GameStates.isLobby)
//            {
//                BMLogger.Error("Exiled() chiamato in lobby. Blocco di sicurezza applicato.", "RPCHandler");
//                return false;
//            }

//            return true;
//        }
//    }
//}
//credits and licenses in the resources folder
using AmongUs.GameOptions;
using BanMod.Modules.CustomHats;
using HarmonyLib;
using Hazel;
using InnerNet;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using static BanMod.Utils;

namespace BanMod
{
    public enum CustomRPC : byte
    {
        ModdedHandshake = 212,
        ProxySendChat = 213,
        SetSpecialKiller = 214,
        SetExiler = 215,
        SetJester = 216,
        SetWatcher = 222,
        SetJudge = 224,
        SetProfiler= 225,
        HostTripleBoolUpdate = 217,
        RoleCommandAction = 218,
        HandshakeModded = 219,
        ModdedAllChat = 220,
        HostRoleOptionsUpdate = 221,
        SyncPlayerVisual = 223,
        Sicko = 164,
        KillNet = 154,
        CREWMODIMPOSTORFORCE = 255
    }

    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.HandleRpc))]
    internal class UnifiedRPCHandlerPatch
    {
        private const string CurrentModVersion = BanMod.PluginVersion;
        private const string LogTag = "RPCHandler";
        private const int MaxRpcDataSize = 128;

        private const int MaxTotalRpcPerWindow = 25;
        private const float TotalRpcWindowSeconds = 0.35f;

        private const int MaxSameRpcPerWindow = 15;
        private const float SameRpcWindowSeconds = 0.15f;

        private const int MaxPlayAnimationPerWindow = 10;
        private const float PlayAnimationWindowSeconds = 0.40f;

        private const int MaxChatMessagesPerWindow = 5;
        private const float ChatWindowSeconds = 2.0f;

        public static readonly Dictionary<byte, string> ModdedClients = new();
        public static readonly HashSet<byte> AlreadyHandledCheaters = new();
        private static readonly HashSet<byte> AlreadyNotifiedModdedClients = new();

        private static readonly Dictionary<int, Queue<float>> GlobalRpcRateByClient = new();
        private static readonly Dictionary<int, Dictionary<int, Queue<float>>> RpcRateByClientAndCall = new();
        private static readonly Dictionary<int, Queue<float>> ChatRateByClient = new();

        private static string GetPlayerName(PlayerControl player)
            => player?.Data?.PlayerName ?? player?.name ?? "Unknown";

        private static string GetPlayerLabel(PlayerControl player)
            => player == null ? "null-player" : $"{GetPlayerName(player)} [pid={player.PlayerId}]";

        public static void ResetModdedNotifications()
        {
            AlreadyNotifiedModdedClients.Clear();
        }

        private static bool IsHostPlayer(PlayerControl player)
        {
            if (player == null || AmongUsClient.Instance == null)
                return false;

            try
            {
                if (player.OwnerId == AmongUsClient.Instance.HostId)
                    return true;
            }
            catch
            {
            }

            try
            {
                if (player.Data != null && player.Data.ClientId == AmongUsClient.Instance.HostId)
                    return true;
            }
            catch
            {
            }

            return false;
        }

        private static void LogToFileOnly(string text)
        {
            BMLogger.LogDebug(text, "RPC-VERBOSE");
        }

        private static string CleanLogText(string text)
        {
            if (string.IsNullOrEmpty(text))
                return string.Empty;

            return text.Replace("\r", "\\r").Replace("\n", "\\n");
        }

        private static int GetConfiguredAction()
        {
            try
            {
                return Options.ActionCheater.GetValue();
            }
            catch
            {
                return 2; // Default to ban
            }
        }

        private static string GetActionLabel(int action)
        {
            if (action == 0) return "warned";
            return action == 2 ? "banned" : "kicked";
        }

        private static string GetRpcName(int callId, bool hasByteId, byte callIdByte)
        {
            if (!hasByteId)
                return $"RPC_INT_{callId}";

            if (Enum.IsDefined(typeof(CustomRPC), callIdByte))
                return $"CustomRPC.{(CustomRPC)callIdByte}";

            if (Enum.IsDefined(typeof(RpcCalls), callIdByte))
                return $"RpcCalls.{(RpcCalls)callIdByte}";

            try
            {
                string mapped = RpcMap.GetRpcName(callIdByte);
                if (!string.IsNullOrWhiteSpace(mapped))
                    return mapped;
            }
            catch
            {
            }

            return $"RPC_BYTE_{callIdByte}";
        }

        private static void RegisterReceivedRpc(PlayerControl player, int callId, bool hasByteId, byte callIdByte, MessageReader reader)
        {
            string rpcName = GetRpcName(callId, hasByteId, callIdByte);
            int length = reader?.Length ?? -1;
            int position = reader?.Position ?? -1;
            int remaining = reader == null ? -1 : length - position;
            int clientId = player != null ? player.GetClientId() : -1;

            LogToFileOnly(
                $"RPC received | Player={GetPlayerLabel(player)} | ClientId={clientId} | " +
                $"CallIdInt={callId} | CallIdByte={(hasByteId ? callIdByte.ToString() : "N/A")} | " +
                $"Name={rpcName} | Position={position} | Length={length} | Remaining={remaining}"
            );
        }

        private static void RegisterReceivedMessage(PlayerControl player, string type, string content)
        {
            LogToFileOnly(
                $"Message received | Type={type} | Player={GetPlayerLabel(player)} | " +
                $"Text={CleanLogText(content)}"
            );
        }

        private static bool RegisterRateHit(
            Dictionary<int, Queue<float>> store,
            int key,
            float now,
            float windowSeconds,
            int maxAllowed,
            out int currentCount)
        {
            if (!store.TryGetValue(key, out var queue))
            {
                queue = new Queue<float>();
                store[key] = queue;
            }

            while (queue.Count > 0 && now - queue.Peek() > windowSeconds)
                queue.Dequeue();

            queue.Enqueue(now);
            currentCount = queue.Count;

            return currentCount > maxAllowed;
        }

        private static bool RegisterPerRpcRateHit(
            int clientId,
            int callId,
            float now,
            float windowSeconds,
            int maxAllowed,
            out int currentCount)
        {
            if (!RpcRateByClientAndCall.TryGetValue(clientId, out var rpcMap))
            {
                rpcMap = new Dictionary<int, Queue<float>>();
                RpcRateByClientAndCall[clientId] = rpcMap;
            }

            if (!rpcMap.TryGetValue(callId, out var queue))
            {
                queue = new Queue<float>();
                rpcMap[callId] = queue;
            }

            while (queue.Count > 0 && now - queue.Peek() > windowSeconds)
                queue.Dequeue();

            queue.Enqueue(now);
            currentCount = queue.Count;

            return currentCount > maxAllowed;
        }

        private static void ShowScreenMessage(string message)
        {
            try
            {
                if (HudManager.Instance?.Notifier != null && !string.IsNullOrWhiteSpace(message))
                    HudManager.Instance.Notifier.AddDisconnectMessage(message);
            }
            catch (Exception ex)
            {
                BMLogger.Warn($"Cannot show screen notification: {ex.Message}", LogTag);
            }
        }

        private static void ApplySuspicionAction(
            PlayerControl player,
            ClientData client,
            int cheaterClientId,
            string banReason,
            string screenMessage,
            bool addBan = true,
            bool addCheater = true,
            bool sendWarning = true)
        {
            if (player == null)
                return;

            int action = GetConfiguredAction();

            byte playerId = player.PlayerId;
            string name = GetPlayerName(player);
            bool firstHandle = AlreadyHandledCheaters.Add(playerId);

            BMLogger.Warn(
                $"Anti-cheat action applied | Player={name} | ClientId={cheaterClientId} | Action={action} | Reason={banReason}",
                LogTag);

            ShowScreenMessage(screenMessage);

            if (client != null && firstHandle)
            {
                try
                {
                    if (addCheater)
                    {
                        CheaterManager.AddPlayer(client, banReason);
                        BMLogger.Info($"Player added to cheater list: {name} | ClientId={cheaterClientId}", LogTag);
                    }
                }
                catch (Exception ex)
                {
                    BMLogger.Error($"Error adding to cheater list: {ex}", LogTag);
                }

                try
                {
                    if (addBan && action == 2)
                    {
                        BanManager.AddBanPlayer(client, banReason);
                        BMLogger.Info($"Player added to ban list: {name} | ClientId={cheaterClientId}", LogTag);
                    }
                }
                catch (Exception ex)
                {
                    BMLogger.Error($"Error adding to ban list: {ex}", LogTag);
                }
            }

            if (firstHandle && sendWarning)
            {
                try
                {
                    AntiCheat.SendGlobalHackWarning();
                }
                catch (Exception ex)
                {
                    BMLogger.Warn($"Cannot send global anti-cheat warning: {ex.Message}", LogTag);
                }
            }

            if (action > 0)
            {
                try
                {
                    if (cheaterClientId >= 0 && AmongUsClient.Instance != null)
                    {
                        AmongUsClient.Instance.KickPlayer(cheaterClientId, action == 2);
                        BMLogger.Warn(
                            $"{(action == 2 ? "Ban" : "Kick")} sent to client | Player={name} | ClientId={cheaterClientId}",
                            LogTag);
                    }
                    else
                    {
                        BMLogger.Warn(
                            $"Cannot apply kick/ban: Invalid ClientId ({cheaterClientId}) | Player={name}",
                            LogTag);
                    }
                }
                catch (Exception ex)
                {
                    BMLogger.Error($"Error executing kick/ban for {name} | ClientId={cheaterClientId}: {ex}", LogTag);
                }
            }
            else
            {
                BMLogger.LogDebug($"Action is 0 (Warning Only) for {name}. No kick/ban applied.", LogTag);
            }
        }

        private static bool IsRpcDataValid(MessageReader reader, int callId, bool isCustom, string playerName)
        {
            if (reader == null)
            {
                BMLogger.Warn($"Invalid RPC: reader is null | Player={playerName} | CallId={callId}", LogTag);
                return false;
            }

            if (reader.Position < 0 || reader.Position > reader.Length)
            {
                BMLogger.Warn(
                    $"Corrupted RPC detected | Player={playerName} | CallId={callId} | Position={reader.Position} | Length={reader.Length}",
                    LogTag);
                return false;
            }

            if (reader.Length > MaxRpcDataSize && !isCustom)
            {
                BMLogger.Error(
                    $"RPC blocked due to excessive size | Player={playerName} | CallId={callId} | Size={reader.Length} | Limit={MaxRpcDataSize}",
                    LogTag);
                return false;
            }

            return true;
        }


        private static bool TryHandleAllowedVisualRpc(
            PlayerControl player,
            MessageReader reader,
            int originalReaderPosition,
            int senderClientId)
        {
            try
            {
                reader.Position = originalReaderPosition;
                MessageReader visualReader = MessageReader.Get(reader);

                PlayerMouseController.ReceiveSyncPlayerVisual(visualReader, senderClientId);

                LogToFileOnly(
                    $"Allowed SyncPlayerVisual RPC handled without anti-cheat rate checks | Player={GetPlayerLabel(player)} | ClientId={senderClientId}");
            }
            catch (Exception ex)
            {
                BMLogger.Warn($"Cannot handle allowed SyncPlayerVisual RPC: {ex}", LogTag);
            }
            finally
            {
                try
                {
                    reader.Position = originalReaderPosition;
                }
                catch
                {
                }
            }

            return true;
        }

        private static bool IsChatDangerous(string text, bool allowLongModdedText = false)
        {
            if (string.IsNullOrEmpty(text))
                return false;

            if (!allowLongModdedText && text.Length > 200)
                return true;

            if (allowLongModdedText && text.Length > 4000)
                return true;

            string lowerText = text.ToLowerInvariant();

            if (lowerText.Contains("<size") ||
                lowerText.Contains("voffset") ||
                lowerText.Contains("<mark") ||
                lowerText.Contains("<material") ||
                lowerText.Contains("<quad"))
            {
                return true;
            }

            int spriteCount = lowerText.Split(new[] { "<sprite" }, StringSplitOptions.None).Length - 1;
            if (spriteCount > 3)
                return true;

            return false;
        }

        private static bool HandleLegacyExploitRpc(
            PlayerControl player,
            ClientData client,
            int cheaterClientId,
            int callId,
            string playerName)
        {
            int action = GetConfiguredAction();

            switch (callId)
            {
                case 101:
                    BMLogger.Warn($"Suspicious legacy RPC 101 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "Legacy_Mod_Detected_101",
                        $"{playerName} {GetActionLabel(action)} for legacy mod/HostGuard (RPC 101).");
                    return true;

                case 150:
                    BMLogger.Warn($"Suspicious RPC 150 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "BetterAmongUs_RPC_150",
                        $"{playerName} {GetActionLabel(action)} for BetterAmongUs (RPC 150).");
                    return true;

                case 176:
                    BMLogger.Warn($"Suspicious RPC 176 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "HostGuard_RPC_176",
                        $"{playerName} {GetActionLabel(action)} for HostGuard (RPC 176).");
                    return true;

                case 250:
                    BMLogger.Warn($"Suspicious RPC 250 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "KillNetwork_RPC_250",
                        $"{playerName} {GetActionLabel(action)} for KillNetwork (RPC 250).");
                    return true;

                case 420:
                    BMLogger.Warn($"Suspicious RPC 420 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "SickoMenu_RPC_420",
                        $"{playerName} {GetActionLabel(action)} for SickoMenu (RPC 420).");
                    return true;

                case 666:
                    BMLogger.Warn($"Suspicious RPC 666 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "GoatNetClient_RPC_666",
                        $"{playerName} {GetActionLabel(action)} for GoatNetClient (RPC 666).");
                    return true;

                case 42069:
                    BMLogger.Warn($"Suspicious RPC 42069 detected from {playerName}.", LogTag);
                    ApplySuspicionAction(
                        player, client, cheaterClientId,
                        "AmongUsMenu_RPC_42069",
                        $"{playerName} {GetActionLabel(action)} for AmongUsMenu (RPC 42069).");
                    return true;
            }

            return false;
        }

        private static string NormalizeModVersion(string modInfo)
        {
            if (string.IsNullOrWhiteSpace(modInfo))
                return "";

            string clean = modInfo.Trim();

            if (clean.Contains("|"))
            {
                string[] parts = clean.Split('|');
                if (parts.Length >= 2)
                    return parts[1].Trim();
            }

            string[] tokens = clean.Split(' ');
            foreach (string token in tokens)
            {
                string t = token.Trim();
                if (System.Text.RegularExpressions.Regex.IsMatch(t, @"^\d+\.\d+(\.\d+)?"))
                    return t;
            }

            return "";
        }

        private static bool IsSameModVersion(string remoteModInfo)
        {
            string remoteVersion = NormalizeModVersion(remoteModInfo);

            if (string.IsNullOrWhiteSpace(remoteVersion)) return false;

            string[] localParts = CurrentModVersion.Split('.');
            string[] remoteParts = remoteVersion.Split('.');

            int compareLength = Math.Min(3, Math.Min(localParts.Length, remoteParts.Length));

            for (int i = 0; i < compareLength; i++)
            {
                if (localParts[i] != remoteParts[i])
                    return false;
            }

            return true;
        }

        private static void BanForWrongModVersion(PlayerControl player, ClientData client, string remoteModInfo)
        {
            if (!AmongUsClient.Instance.AmHost)
                return;

            if (player == null)
                return;

            int clientId = player.GetClientId();
            string playerName = GetPlayerName(player);
            string remoteVersion = NormalizeModVersion(remoteModInfo);

            string reason = string.IsNullOrWhiteSpace(remoteVersion)
                ? "Wrong_Mod_Version_Missing"
                : $"Wrong_Mod_Version_{remoteVersion}_Expected_{CurrentModVersion}";

            BMLogger.Warn(
                $"Different mod version detected | Player={playerName} | ClientId={clientId} | RemoteInfo={remoteModInfo} | RemoteVersion={remoteVersion} | Expected={CurrentModVersion}",
                LogTag
            );

            try
            {
                ShowScreenMessage($"{playerName} banned: different BANMOD version.");
            }
            catch
            {
            }

            try
            {
                if (clientId >= 0)
                {
                    AmongUsClient.Instance.KickPlayer(clientId, false);
                    BMLogger.Warn($"Ban sent for different mod version | Player={playerName} | ClientId={clientId}", LogTag);
                }
            }
            catch (Exception ex)
            {
                BMLogger.Error($"Error banning for different version | Player={playerName} | ClientId={clientId} | {ex}", LogTag);
            }
        }

        private static void DisconnectForWrongHostModVersion(string hostModInfo)
        {
            try
            {
                if (AmongUsClient.Instance == null)
                    return;

                string hostVersion = NormalizeModVersion(hostModInfo);

                BMLogger.Warn(
                    $"Host with incompatible BANMOD version | HostInfo={hostModInfo} | HostVersion={hostVersion} | Expected={CurrentModVersion}",
                    LogTag
                );

                try
                {
                    ShowScreenMessage(
                        $"Incompatible lobby: host BANMOD {hostVersion}, your version {CurrentModVersion}."
                    );
                }
                catch
                {
                }

                AmongUsClient.Instance.ExitGame(DisconnectReasons.Custom);
            }
            catch (Exception ex)
            {
                BMLogger.Error($"Error during disconnect for different host version: {ex}", LogTag);
            }
        }

        public static bool Prefix(PlayerControl __instance, [HarmonyArgument(0)] int callId, [HarmonyArgument(1)] MessageReader reader)
        {
            if (BanMod.IsBanModDisabled) return true;

            if (__instance == null)
            {
                BMLogger.Warn("HandleRpc intercepted with null PlayerControl.", LogTag);
                return true;
            }

            if (reader == null)
            {
                BMLogger.Warn($"HandleRpc intercepted with null reader | Player={GetPlayerLabel(__instance)} | CallIdInt={callId}", LogTag);
                return false;
            }

            bool hasByteId = callId >= byte.MinValue && callId <= byte.MaxValue;
            byte callIdByte = hasByteId ? (byte)callId : byte.MaxValue;

            RegisterReceivedRpc(__instance, callId, hasByteId, callIdByte, reader);

            if (PlayerControl.LocalPlayer == __instance)
                return true;

            string name = GetPlayerName(__instance);
            int cheaterClientId = __instance.GetClientId();
            ClientData client = ExtendedPlayerControl.GetClient(__instance);
            int action = GetConfiguredAction();
            float now = Time.realtimeSinceStartup;

            int originalReaderPosition = reader.Position;

            try
            {
                if (HandleLegacyExploitRpc(__instance, client, cheaterClientId, callId, name))
                    return false;

                if (!hasByteId)
                {
                    BMLogger.Warn(
                        $"RPC with CallId out of byte range detected | Player={name} | CallIdInt={callId}",
                        LogTag);

                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "RPC_CallId_Out_Of_Range",
                        $"{name} {GetActionLabel(action)} for RPC with invalid identifier."
                    );

                    return false;
                }

                bool isCustom = Enum.IsDefined(typeof(CustomRPC), callIdByte);
                bool isStandard = Enum.IsDefined(typeof(RpcCalls), callIdByte);

                if (!IsRpcDataValid(reader, callId, isCustom, name))
                {
                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "Suspect_Corrupted_RPC",
                        $"{name} {GetActionLabel(action)} for malformed RPC.",
                        addBan: true,
                        addCheater: true,
                        sendWarning: true
                    );

                    return false;
                }

                // SyncPlayerVisual is used only for cosmetic/body visual sync such as resize/body type.
                // Handle it before flood/suspicion checks so legitimate visual changes are not flagged as cheat.
                if (callIdByte == (byte)CustomRPC.SyncPlayerVisual)
                {
                    TryHandleAllowedVisualRpc(__instance, reader, originalReaderPosition, cheaterClientId);
                    return false;
                }

                if (callIdByte == (byte)RpcCalls.LobbyTimeExpiring)
                {
                    if (!AmongUsClient.Instance.AmHost || !IsHostPlayer(__instance))
                    {
                        BMLogger.Warn($"Tentativo di manipolazione timer bloccato da {name}.", "RPCHandler");
                        ApplySuspicionAction(__instance, client, cheaterClientId,
                            "Timer_Manipulation_Attempt",
                            $"{name} {GetActionLabel(action)} per manipolazione timer.");
                        return false; // Blocca il pacchetto
                    }
                }

                if (cheaterClientId >= 0 &&
                    RegisterRateHit(
                        GlobalRpcRateByClient,
                        cheaterClientId,
                        now,
                        TotalRpcWindowSeconds,
                        MaxTotalRpcPerWindow,
                        out int totalRpcCount))
                {
                    BMLogger.Warn(
                        $"Total RPC flood detected | Player={name} | ClientId={cheaterClientId} | " +
                        $"Count={totalRpcCount} in {TotalRpcWindowSeconds:0.00}s",
                        LogTag);

                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "Global_RPC_Flood_Window",
                        $"{name} {GetActionLabel(action)} for massive RPC flood."
                    );

                    return false;
                }

                if (cheaterClientId >= 0)
                {
                    int sameRpcLimit = callIdByte == (byte)RpcCalls.PlayAnimation
                        ? MaxPlayAnimationPerWindow
                        : MaxSameRpcPerWindow;

                    float sameRpcWindow = callIdByte == (byte)RpcCalls.PlayAnimation
                        ? PlayAnimationWindowSeconds
                        : SameRpcWindowSeconds;

                    bool sameRpcFlood = RegisterPerRpcRateHit(
                        cheaterClientId,
                        callId,
                        now,
                        sameRpcWindow,
                        sameRpcLimit,
                        out int sameRpcCount);

                    LogToFileOnly(
                        $"RPC history | Player={GetPlayerLabel(__instance)} | CallIdInt={callId} | " +
                        $"WindowCount={sameRpcCount} | Window={sameRpcWindow:0.00}s"
                    );

                    if (sameRpcFlood)
                    {
                        string reasonCode = callIdByte == (byte)RpcCalls.PlayAnimation
                            ? "RPC_PlayAnimation_Abuse"
                            : "Suspect_Cheater_RPCFlood";

                        string reasonText = callIdByte == (byte)RpcCalls.PlayAnimation
                            ? "PlayAnimation abuse"
                            : $"RPC flood {GetRpcName(callId, true, callIdByte)}";

                        BMLogger.Warn(
                            $"Specific RPC flood detected | Player={name} | CallId={callId} | Count={sameRpcCount}",
                            LogTag);

                        ApplySuspicionAction(
                            __instance,
                            client,
                            cheaterClientId,
                            reasonCode,
                            $"{name} {GetActionLabel(action)} for {reasonText}.",
                            addBan: true,
                            addCheater: true,
                            sendWarning: true
                        );

                        return false;
                    }
                }

                if (callIdByte != (byte)CustomRPC.ModdedAllChat &&
                    callIdByte != (byte)CustomRPC.ProxySendChat &&
                    callIdByte != (byte)CustomRPC.RoleCommandAction &&
                    callIdByte != (byte)CustomRPC.SetSpecialKiller &&
                    callIdByte != (byte)CustomRPC.SetExiler &&
                    callIdByte != (byte)CustomRPC.SetJester &&
                    callIdByte != (byte)CustomRPC.SetWatcher &&
                    callIdByte != (byte)CustomRPC.SetJudge &&
                    callIdByte != (byte)CustomRPC.SetProfiler &&
                    callIdByte != (byte)CustomRPC.SyncPlayerVisual &&
                    callIdByte != (byte)CustomRPC.HostTripleBoolUpdate &&
                    callIdByte != (byte)CustomRPC.HostRoleOptionsUpdate &&
                    callIdByte != (byte)CustomRPC.HandshakeModded &&
                    callIdByte != (byte)CustomHatSync.RpcId &&
                    callIdByte != (byte)CustomRPC.ModdedHandshake)
                {
                    try
                    {
                        reader.Position = originalReaderPosition;
                        AntiCheat.PlayerControlReceiveRpc(__instance, callIdByte, reader);
                    }
                    finally
                    {
                        reader.Position = originalReaderPosition;
                    }
                }

                MessageReader subReader = MessageReader.Get(reader);

                LogToFileOnly(
                    $"RPC details | Player={GetPlayerLabel(__instance)} | RpcName={GetRpcName(callId, true, callIdByte)}"
                );

                if (AmongUsClient.Instance.AmHost &&
                    callIdByte == (byte)RpcCalls.Exiled &&
                    MeetingHud.Instance == null &&
                    !__instance.AmOwner)
                {
                    BMLogger.Warn($"Blocked Exiled RPC outside meeting from {name}.", LogTag);

                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "Crash_RPC_Exiled",
                        $"{name} {GetActionLabel(action)} for suspicious Exiled RPC."
                    );

                    return false;
                }

                if (AmongUsClient.Instance.AmHost &&
                    callIdByte == (byte)RpcCalls.VotingComplete &&
                    GameStates.isLobby &&
                    MeetingHud.Instance == null)
                {
                    BMLogger.Warn($"Blocked VotingComplete RPC in lobby from {name}.", LogTag);

                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "Crash_VotingComplete_Lobby",
                        $"{name} {GetActionLabel(action)} for suspicious VotingComplete in lobby."
                    );

                    return false;
                }

                if (callIdByte == CustomHatSync.RpcId)
                {
                    try
                    {
                        CustomHatSync.ReceiveRpc(subReader);
                    }
                    catch (Exception ex)
                    {
                        BMLogger.Warn($"Invalid CustomHatSync RPC 240 from {name}: {ex.Message}", LogTag);
                    }

                    return false;
                }

                if (!isCustom && !isStandard)
                {
                    LogToFileOnly(
                        $"Unknown RPC received | Player={GetPlayerLabel(__instance)} | CallIdInt={callId} | CallIdByte={callIdByte}"
                    );

                    ApplySuspicionAction(
                        __instance,
                        client,
                        cheaterClientId,
                        "Unknown_RPC",
                        $"{name} {GetActionLabel(action)} for unknown RPC ({callIdByte})."
                    );

                    return false;
                }

                if (isCustom)
                {
                    var customType = (CustomRPC)callIdByte;
                    LogToFileOnly($"Handling CustomRPC | Player={GetPlayerLabel(__instance)} | Type={customType}");

                    switch (customType)
                    {
                        case CustomRPC.HandshakeModded:
                            {
                                RegisterModdedClient(__instance.PlayerId, "BanMod");

                                LogToFileOnly($"HandshakeModded registered for {GetPlayerLabel(__instance)}.");
                                return false;
                            }

                        case CustomRPC.ModdedHandshake:
                            {
                                string modInfo = subReader.ReadString();
                                bool isRemoteModded = subReader.ReadBoolean();
                                byte playerId = __instance.PlayerId;

                                string cleanModInfo = string.IsNullOrWhiteSpace(modInfo) ? "BanMod" : modInfo;

                                if (isRemoteModded)
                                {
                                    RegisterModdedClient(playerId, cleanModInfo);
                                }

                                LogToFileOnly(
                                    $"ModdedHandshake received | Player={GetPlayerLabel(__instance)} | ModInfo={CleanLogText(cleanModInfo)} | FlagModded={isRemoteModded}"
                                );

                                if (AmongUsClient.Instance != null &&
                                    AmongUsClient.Instance.AmHost &&
                                    isRemoteModded &&
                                    !IsHostPlayer(__instance))
                                {
                                    if (!IsSameModVersion(cleanModInfo))
                                    {
                                        BanForWrongModVersion(__instance, client, cleanModInfo);
                                        return false;
                                    }
                                }

                                if (AmongUsClient.Instance != null &&
                                    !AmongUsClient.Instance.AmHost &&
                                    isRemoteModded &&
                                    IsHostPlayer(__instance))
                                {
                                    if (!IsSameModVersion(cleanModInfo))
                                    {
                                        DisconnectForWrongHostModVersion(cleanModInfo);
                                        return false;
                                    }
                                }

                                if (isRemoteModded &&
                                    AlreadyNotifiedModdedClients.Add(playerId) &&
                                    HudManager.Instance?.Notifier != null)
                                {
                                    HudManager.Instance.Notifier.AddDisconnectMessage(
                                        $"Modded client connected: {GetPlayerName(__instance)} | Mod: {cleanModInfo}"
                                    );
                                }

                                return false;
                            }

                        case CustomRPC.HostTripleBoolUpdate:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"HostTripleBoolUpdate BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                bool remoteBool1ImmortalAdded = subReader.ReadBoolean();
                                bool remoteBool2ImmortalEnabled = subReader.ReadBoolean();
                                bool remoteBool3EngineerEnabled = subReader.ReadBoolean();

                                HostOptionStatus.UpdateHostRules(
                                    remoteBool1ImmortalAdded,
                                    remoteBool2ImmortalEnabled,
                                    remoteBool3EngineerEnabled
                                );

                                LogToFileOnly(
                                    $"HostTripleBoolUpdate received | Added={remoteBool1ImmortalAdded} | Enabled={remoteBool2ImmortalEnabled} | Engineer={remoteBool3EngineerEnabled}"
                                );

                                return false;
                            }

                        case CustomRPC.ProxySendChat:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"ProxySendChat BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                string message = subReader.ReadString();
                                string ignoredTitle = subReader.ReadString();
                                byte target = subReader.ReadByte();

                                RegisterReceivedMessage(__instance, "ProxySendChat", message);

                                LogToFileOnly(
                                    $"ProxySendChat received | IgnoredTitle={CleanLogText(ignoredTitle)} | TargetPlayerId={target}");

                                PlayerControl localPlayer = PlayerControl.LocalPlayer;

                                if (localPlayer == null || localPlayer.Data == null)
                                    return false;

                                if (localPlayer.Data.IsDead || localPlayer.Data.Disconnected)
                                    return false;

                                if (string.IsNullOrWhiteSpace(message))
                                    return false;

                                message = message
                                    .RemoveHtmlTags()
                                    .Replace("\\r\\n", "\n")
                                    .Replace("\\n", "\n")
                                    .Replace("\\r", "\n")
                                    .Replace("\r\n", "\n")
                                    .Replace("\r", "\n")
                                    .Trim();

                                if (string.IsNullOrWhiteSpace(message))
                                    return false;

                                if (message.Length > 120)
                                    message = message.Substring(0, 120);

                                bool sendToAll = target == byte.MaxValue || target == 255;
                                bool targetIsProxySelf = target == localPlayer.PlayerId;

                                int targetClientId = -1;

                                if (!sendToAll && !targetIsProxySelf)
                                {
                                    PlayerControl targetPlayer = Utils.GetPlayerById(target);

                                    if (targetPlayer == null || targetPlayer.Data == null || targetPlayer.Data.Disconnected)
                                        return false;

                                    targetClientId = targetPlayer.GetClientId();

                                    if (targetClientId < 0)
                                        return false;
                                }

                                if (targetIsProxySelf)
                                {
                                    try
                                    {
                                        DestroyableSingleton<HudManager>.Instance.Chat.AddChat(localPlayer, message, false);
                                        MessageBlocker.UpdateLastMessageTime();

                                        LogToFileOnly(
                                            $"ProxySendChat shown locally to the proxy itself | Proxy={GetPlayerLabel(localPlayer)} | TargetPlayerId={target} | Text={CleanLogText(message)}");
                                    }
                                    catch (Exception ex)
                                    {
                                        BMLogger.Warn($"ProxySendChat local AddChat failed: {ex}", LogTag);
                                    }

                                    return false;
                                }

                                if (!MessageBlocker.CanSendMessage())
                                {
                                    ProxyMessageQueue.Enqueue(message, targetClientId, sendToAll);

                                    LogToFileOnly(
                                        $"ProxySendChat queued for cooldown | TargetPlayerId={target} | TargetClientId={targetClientId} | SendToAll={sendToAll} | Text={CleanLogText(message)}");

                                    return false;
                                }

                                try
                                {
                                    var writer = CustomRpcSender.Create("ProxySendChatDirect", SendOption.Reliable);

                                    writer.StartMessage(targetClientId);
                                    writer.StartRpc(localPlayer.NetId, (byte)RpcCalls.SendChat)
                                        .Write(message)
                                        .EndRpc();
                                    writer.EndMessage();
                                    writer.SendMessage();

                                    if (sendToAll)
                                    {
                                        try
                                        {
                                            DestroyableSingleton<HudManager>.Instance.Chat.AddChat(localPlayer, message, false);
                                        }
                                        catch (Exception ex)
                                        {
                                            BMLogger.Warn($"ProxySendChat local broadcast AddChat failed: {ex}", LogTag);
                                        }
                                    }

                                    MessageBlocker.UpdateLastMessageTime();

                                    LogToFileOnly(
                                        $"ProxySendChat sent | Proxy={GetPlayerLabel(localPlayer)} | TargetPlayerId={target} | TargetClientId={targetClientId} | SendToAll={sendToAll} | Text={CleanLogText(message)}");
                                }
                                catch (Exception ex)
                                {
                                    BMLogger.Warn($"ProxySendChat send failed: {ex}", LogTag);
                                }

                                return false;
                            }
                        case CustomRPC.SyncPlayerVisual:
                            {
                                PlayerMouseController.ReceiveSyncPlayerVisual(subReader, cheaterClientId);
                                return false;
                            }
                        case CustomRPC.ModdedAllChat:
                            {
                                byte senderId = subReader.ReadByte();
                                string text = subReader.ReadString();

                                RegisterReceivedMessage(__instance, "ModdedAllChat", text);

                                if (IsChatDangerous(text, true))
                                {
                                    BMLogger.Warn(
                                        $"Dangerous ModdedAllChat message blocked from {name}: {CleanLogText(text)}",
                                        LogTag);

                                    return false;
                                }

                                if (senderId != __instance.PlayerId)
                                {
                                    BMLogger.Warn(
                                        $"ModdedAllChat senderId mismatch | RPC owner={__instance.PlayerId} | senderId={senderId}",
                                        LogTag);

                                    return false;
                                }

                                ModdedOriginalChatManager.ReceiveAll(senderId, text);

                                try
                                {
                                    if (HudManager.Instance != null &&
                                        HudManager.Instance.Chat != null &&
                                        !HudManager.Instance.Chat.IsOpenOrOpening)
                                    {
                                        ChatControllerUpdatePatch.TryFlashChatNotifyDot();
                                    }
                                }
                                catch
                                {
                                }

                                return false;
                            }
                        case CustomRPC.HostRoleOptionsUpdate:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"HostRoleOptionsUpdate BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                HostRoleOptionsRpc.Receive(__instance, subReader);
                                return false;
                            }

                        case CustomRPC.RoleCommandAction:
                            {
                                RoleCommandActionRpc.Receive(__instance, subReader);
                                return false;
                            }

                        case CustomRPC.SetSpecialKiller:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetSpecialKiller BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte killerId = subReader.ReadByte();

                                Guesser.SpecialKillerId = killerId;
                                Guesser.SpecialKillerSelected = killerId != byte.MaxValue && killerId != 255;

                                LogToFileOnly($"SetSpecialKiller received | KillerId={killerId}");

                                try
                                {
                                    RoleButtonRefresh.RefreshNow();
                                }
                                catch
                                {
                                }

                                return false;
                            }

                        case CustomRPC.SetJester:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetJester BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte jesterId = subReader.ReadByte();

                                Jester.JesterId = jesterId;
                                Jester.JesterSelected = jesterId != byte.MaxValue && jesterId != 255;

                                try
                                {
                                    Jester.ForcedJesterId = jesterId;
                                    Jester.ForcedJesterSelected = jesterId != byte.MaxValue && jesterId != 255;
                                }
                                catch
                                {
                                }

                                LogToFileOnly($"SetJester received | JesterId={jesterId}");

                                try
                                {
                                    RoleButtonRefresh.RefreshNow();
                                }
                                catch
                                {
                                }

                                return false;
                            }

                        case CustomRPC.SetExiler:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetExiler BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte exilerId = subReader.ReadByte();

                                Exiler.ExilerId = exilerId;
                                Exiler.ExilerSelected = exilerId != byte.MaxValue && exilerId != 255;

                                LogToFileOnly($"SetExiler received | ExilerId={exilerId}");

                                try
                                {
                                    RoleButtonRefresh.RefreshNow();
                                }
                                catch
                                {
                                }

                                return false;
                            }
                        case CustomRPC.SetJudge:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetJudge BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte JudgeId = subReader.ReadByte();

                                Judge.JudgeId = JudgeId;
                                Judge.JudgeSelected = JudgeId != byte.MaxValue && JudgeId != 255;

                                LogToFileOnly($"SetJudge received | JudgeId={JudgeId}");

                                try
                                {
                                    RoleButtonRefresh.RefreshNow();
                                }
                                catch
                                {
                                }

                                return false;
                            }
                        case CustomRPC.SetProfiler:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetProfiler BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte ProfilerId = subReader.ReadByte();

                                Profiler.ProfilerId = ProfilerId;
                                Profiler.ProfilerSelected = ProfilerId != byte.MaxValue && ProfilerId != 255;

                                LogToFileOnly($"SetProfiler received | ProfilerId={ProfilerId}");

                                try
                                {
                                    RoleButtonRefresh.RefreshNow();
                                }
                                catch
                                {
                                }

                                return false;
                            }
                        case CustomRPC.SetWatcher:
                            {
                                PlayerControl sender = __instance;

                                if (!IsHostPlayer(sender))
                                {
                                    LogToFileOnly(
                                        $"SetWatcher BLOCKED: sender not host | Sender={GetPlayerLabel(sender)} | SenderPlayerId={sender?.PlayerId} | SenderClientId={sender?.GetClientId()} | HostId={AmongUsClient.Instance?.HostId}");

                                    return false;
                                }
                                byte watcherId = subReader.ReadByte();

                                Watcher.WatcherId = watcherId;
                                Watcher.WatcherSelected = watcherId != byte.MaxValue && watcherId != 255;

                                LogToFileOnly($"SetWatcher received | WatcherId={watcherId}");

                                return false;
                            }
                        case CustomRPC.Sicko:
                            {
                                ApplySuspicionAction(
                                    __instance,
                                    client,
                                    cheaterClientId,
                                    "Suspect_Cheater_SickoMenu",
                                    $"{name} {GetActionLabel(action)} for SickoMenu."
                                );

                                return false;
                            }

                        case CustomRPC.CREWMODIMPOSTORFORCE:
                            {
                                ApplySuspicionAction(
                                    __instance,
                                    client,
                                    cheaterClientId,
                                    "Suspect_CREWMODIMPOSTORFORCE",
                                    $"{name} {GetActionLabel(action)} for CREWMODIMPOSTORFORCE."
                                );

                                return false;
                            }

                        case CustomRPC.KillNet:
                            {
                                ApplySuspicionAction(
                                    __instance,
                                    client,
                                    cheaterClientId,
                                    "Suspect_Cheater_KillNet",
                                    $"{name} {GetActionLabel(action)} for KillNetWork Hack."
                                );

                                return false;
                            }
                    }

                    return false;
                }

                var rpcType = (RpcCalls)callIdByte;
                LogToFileOnly($"Handling standard RpcCalls | Player={GetPlayerLabel(__instance)} | Type={rpcType}");

                switch (rpcType)
                {
                    case RpcCalls.SendChat:
                        {
                            string text = subReader.ReadString();
                            if (text.Contains("hacked by") || text.Contains("discord.gg") || text.Contains("bombsaddicts"))
                            {
                                ApplySuspicionAction(__instance, client, cheaterClientId,
                                    "Chat_Spam_Hack",
                                    $"{name} {GetActionLabel(action)} per spam in chat.");
                                return false; // Blocca l'invio del messaggio
                            }
                            RegisterReceivedMessage(__instance, "SendChat", text);

                            if (cheaterClientId >= 0 &&
                                RegisterRateHit(
                                    ChatRateByClient,
                                    cheaterClientId,
                                    now,
                                    ChatWindowSeconds,
                                    MaxChatMessagesPerWindow,
                                    out int chatCount))
                            {
                                BMLogger.Warn(
                                    $"Chat flood detected | Player={name} | ClientId={cheaterClientId} | Count={chatCount} in {ChatWindowSeconds:0.0}s",
                                    LogTag);

                                ApplySuspicionAction(
                                    __instance,
                                    client,
                                    cheaterClientId,
                                    "Chat_Flood",
                                    $"{name} {GetActionLabel(action)} for chat flood."
                                );

                                return false;
                            }

                            if (IsChatDangerous(text))
                            {
                                BMLogger.Warn($"Dangerous chat message blocked from {name}: {CleanLogText(text)}", LogTag);

                                ApplySuspicionAction(
                                    __instance,
                                    client,
                                    cheaterClientId,
                                    "Crash_Chat_Formatting",
                                    $"{name} {GetActionLabel(action)} for dangerous chat message."
                                );

                                return false;
                            }

                            ChatCommands.OnReceiveChat(__instance, text, out var canceled);
                            if (canceled)
                            {
                                LogToFileOnly($"Chat message cancelled by commands | Player={GetPlayerLabel(__instance)}");
                                return false;
                            }

                            break;
                        }
                    case RpcCalls.SendQuickChat:
                        {
                            // 1. Controllo Flood (Anti-Spam)
                            if (cheaterClientId >= 0 &&
                                RegisterRateHit(
                                    ChatRateByClient,
                                    cheaterClientId,
                                    now,
                                    ChatWindowSeconds,
                                    MaxChatMessagesPerWindow,
                                    out int quickChatCount))
                            {
                                BMLogger.Warn($"QuickChat flood rilevato da {name}", LogTag);
                                ApplySuspicionAction(__instance, client, cheaterClientId,
                                    "QuickChat_Flood",
                                    $"{name} {GetActionLabel(action)} per flood QuickChat.");
                                return false; // Blocca tutto se floodano
                            }

                            // 2. Logging ed elaborazione comandi
                            RegisterReceivedMessage(__instance, "SendQuickChat", "Quick chat");
                            ChatCommands.OnReceiveChat(__instance, "Quick chat", out var canceledQuick);

                            if (canceledQuick)
                            {
                                LogToFileOnly($"Quick chat cancelled by commands | Player={GetPlayerLabel(__instance)}");
                                return false;
                            }

                            break; // Uscita dal case
                        }

                    default:
                        break;
                }

                return true;
            }
            catch (Exception ex)
            {
                BMLogger.Error(
                    $"Error during RPC handling | Player={name} | CallId={callId} | Error={ex}",
                    LogTag);

                ApplySuspicionAction(
                    __instance,
                    client,
                    cheaterClientId,
                    "RPC_Exception",
                    $"{name} {GetActionLabel(action)} for malformed RPC or crash attempt."
                );

                return false;
            }
            finally
            {
                try
                {
                    reader.Position = originalReaderPosition;
                }
                catch (Exception ex)
                {
                    BMLogger.Warn($"Cannot restore reader position: {ex.Message}", LogTag);
                }
            }
        }

        public static void RegisterModdedClient(byte playerId, string modInfo = "BanMod")
        {
            try
            {
                if (!ModdedRegistry.ModdedPlayers.Contains(playerId))
                    ModdedRegistry.ModdedPlayers.Add(playerId);
            }
            catch
            {
            }

            if (string.IsNullOrWhiteSpace(modInfo))
                modInfo = "BanMod";

            ModdedClients[playerId] = modInfo;
        }

        public static void RegisterLocalAsModded()
        {
            try
            {
                if (PlayerControl.LocalPlayer == null)
                    return;

                string modInfo = AmongUsClient.Instance != null && AmongUsClient.Instance.AmHost
                    ? "BanMod Host"
                    : "BanMod";

                RegisterModdedClient(PlayerControl.LocalPlayer.PlayerId, modInfo);
            }
            catch (Exception)
            {

            }
        }

        public static void SendLocalModdedHandshake()
        {
            try
            {
                if (AmongUsClient.Instance == null)
                    return;

                if (PlayerControl.LocalPlayer == null)
                    return;

                if (PlayerControl.LocalPlayer.NetId == 0)
                    return;

                RegisterLocalAsModded();

                string modInfo = AmongUsClient.Instance.AmHost
                    ? $"BANMOD|{CurrentModVersion}|HOST"
                    : $"BANMOD|{CurrentModVersion}";

                var writer = AmongUsClient.Instance.StartRpcImmediately(
                    PlayerControl.LocalPlayer.NetId,
                    (byte)CustomRPC.ModdedHandshake,
                    SendOption.Reliable,
                    -1);

                writer.Write(modInfo);
                writer.Write(true);

                AmongUsClient.Instance.FinishRpcImmediately(writer);
            }
            catch
            {
            }
        }

        public static bool IsClientModded(byte playerId)
        {
            if (ModdedClients.ContainsKey(playerId))
                return true;

            try
            {
                if (ModdedRegistry.ModdedPlayers.Contains(playerId))
                    return true;
            }
            catch
            {
            }

            return false;
        }

        public static List<(PlayerControl player, string modInfo)> GetModdedPlayersWithInfo()
        {
            return PlayerControl.AllPlayerControls
                .ToArray()
                .Where(p => p != null && IsClientModded(p.PlayerId))
                .Select(p =>
                {
                    string modInfo = "BanMod";

                    if (ModdedClients.TryGetValue(p.PlayerId, out string savedInfo) &&
                        !string.IsNullOrWhiteSpace(savedInfo))
                    {
                        modInfo = savedInfo;
                    }

                    return (p, modInfo);
                })
                .ToList();
        }
    }

    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Start))]
    internal static class PlayerControl_Start_ModdedHandshakePatch
    {
        private static bool SentHandshake;

        public static void Postfix(PlayerControl __instance)
        {
            try
            {
                if (__instance == null)
                    return;

                if (PlayerControl.LocalPlayer == null)
                    return;

                if (__instance != PlayerControl.LocalPlayer)
                    return;

                if (SentHandshake)
                    return;

                SentHandshake = true;

                ModdedHandshakeAutoSender.ResetHandshakeBurst();
                ModdedHandshakeAutoSender.TrySendHandshakeNow(true);
            }
            catch (Exception)
            {

            }
        }

        public static void Reset()
        {
            SentHandshake = false;
        }
    }

    internal static class ModdedHandshakeAutoSender
    {
        private const float HandshakeIntervalSeconds = 3f;
        private const int MaxHandshakeBurstSends = 8;

        private static float _nextHandshakeTime = 0f;
        private static int _handshakeSends = 0;

        public static void ResetHandshakeBurst()
        {
            _nextHandshakeTime = 0f;
            _handshakeSends = 0;
        }

        public static void TrySendHandshakeNow(bool force = false)
        {
            try
            {
                if (AmongUsClient.Instance == null)
                    return;

                if (PlayerControl.LocalPlayer == null)
                    return;

                if (!force)
                {
                    if (_handshakeSends >= MaxHandshakeBurstSends)
                        return;

                    if (Time.realtimeSinceStartup < _nextHandshakeTime)
                        return;
                }

                _nextHandshakeTime = Time.realtimeSinceStartup + HandshakeIntervalSeconds;
                _handshakeSends++;

                UnifiedRPCHandlerPatch.SendLocalModdedHandshake();
            }
            catch (Exception ex)
            {
                BMLogger.Warn($"[Handshake] TrySendHandshakeNow failed: {ex}", "RPCHandler");
            }
        }
    }

    [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.Update))]
    internal static class AmongUsClient_Update_ModdedHandshakePatch
    {
        public static void Postfix()
        {
            try
            {
                if (AmongUsClient.Instance == null)
                    return;

                if (PlayerControl.LocalPlayer == null)
                    return;

                if (!GameStates.isOnlineGame)
                    return;

                ModdedHandshakeAutoSender.TrySendHandshakeNow(false);
            }
            catch
            {
            }
        }
    }

    [HarmonyPatch(typeof(AmongUsClient), nameof(AmongUsClient.OnGameJoined))]
    internal static class AmongUsClient_OnGameJoined_ModdedHandshakePatch
    {
        public static void Postfix()
        {
            try
            {
                UnifiedRPCHandlerPatch.ModdedClients.Clear();

                try
                {
                    ModdedRegistry.ModdedPlayers.Clear();
                    UnifiedRPCHandlerPatch.ResetModdedNotifications();
                    HostRoleOptionsStatus.Reset();
                }
                catch
                {
                }

                PlayerControl_Start_ModdedHandshakePatch.Reset();

                UnifiedRPCHandlerPatch.RegisterLocalAsModded();

                ModdedHandshakeAutoSender.ResetHandshakeBurst();
                ModdedHandshakeAutoSender.TrySendHandshakeNow(true);
            }
            catch (Exception)
            {

            }
        }
    }


    [HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.Exiled))]
    internal class ExiledLobbyGuardPatch
    {
        static bool Prefix(PlayerControl __instance)
        {
            if (GameStates.isLobby)
            {
                BMLogger.Error("Exiled() called in lobby. Security block applied.", "RPCHandler");
                return false;
            }

            return true;
        }
    }
}