Иконка ресурса

FREE AnticheatPlus : 1.1.5 1.1.5

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using System;
using System.Threading;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System.Collections.Generic;
using System.Timers;
using Newtonsoft.Json;
using UnityEngine;
using System.Linq;
using Oxide.Core.Libraries;


namespace Oxide.Plugins
{
    [Info("Anticheat+", "Nord", "1.1.5")]
    [Description("An extra layer of protection for your rust server!")]

    public class AnticheatPlus : RustPlugin

    {


        private Dictionary<BasePlayer, ViolationCount> cachedViolationsShootInAir = new Dictionary<BasePlayer, ViolationCount>();
        private Dictionary<BasePlayer, ViolationCount> cachedViolationsShootMaxDistance = new Dictionary<BasePlayer, ViolationCount>();
        private Dictionary<BasePlayer, ViolationCount> cachedViolationsFlyhackViolation = new Dictionary<BasePlayer, ViolationCount>();
        private Dictionary<string, List<int>> playerPingHistory = new Dictionary<string, List<int>>();
        private Dictionary<ulong, DateTime> elevatedPlayers = new Dictionary<ulong, DateTime>();

        private class ViolationCount
        {
            public int Count { get; set; }
            public long TimeOfLastViolation { get; set; }
        }
        public DateTime TimeOfViolation { get; set; }
        private BasePlayer lastFlaggedPlayer = null;
        private Vector3 excavatorPosition;


        private Timer _checkPlayersTimer;
        private Timer _checkPlayersPingTimer;
        private Timer _checkServerStatusTimer;

        private DateTime lastMessageSent = DateTime.MinValue;
        private TimeSpan messageCooldown => TimeSpan.FromSeconds(_config.WebhookCooldown);
        private const string DataFileName = "AnticheatPlusAdminData";
        private Dictionary<string, bool> AdminPreferences = new Dictionary<string, bool>();
        private void LoadAdminPreferences() { AdminPreferences = Interface.Oxide.DataFileSystem.ReadObject<Dictionary<string, bool>>(DataFileName) ?? new Dictionary<string, bool>(); }
        private void SaveAdminPreferences() { Interface.Oxide.DataFileSystem.WriteObject(DataFileName, AdminPreferences); }

        #region Configuration
        protected override void LoadDefaultConfig() => _config = new Configuration(); protected override void LoadConfig() { base.LoadConfig(); _config = Config.ReadObject<Configuration>(); SaveConfig(); }
        protected override void SaveConfig() => Config.WriteObject(_config);
        private class Configuration

        {
            [JsonProperty("Debug Mode (will cause console spam.)")]
            public bool debugMode { get; private set; } = false;

            [JsonProperty("Anticheat Throttling (will disable some anticheat features if server fps has fell below 13 FPS.)")]
            public bool acThrottle { get; private set; } = false;

            [JsonProperty("Anticheat+ Update Time (default: 5 seconds")]
            public float AnticheatCheckSpeed { get; private set; } = 5;

            [JsonProperty("Kick When Flagged")]
            public bool KickUponFlag { get; private set; } = false;

            [JsonProperty("Flag Admins")]
            public bool FlagAdmins { get; private set; } = false;

            [JsonProperty("Alert In-Game Admins (with anticheatplus.admin permission)")]
            public bool AlertIngameAdmins { get; private set; } = true;
            [JsonProperty("Enable AntiLagSwitch (beta)")]
            public bool AntiLagSwitch { get; private set; } = true;

            [JsonProperty("Enable Flyhack+ (beta)")]
            public bool BetterFlyhack { get; private set; } = true;

            [JsonProperty("Flyhack+ Threshold (in meters, default is 7)")]
            public float BetterFlyhackThreshold { get; private set; } = 7;

            [JsonProperty("Flyhack+ Threshold Time (time the user is exceeding the height threshold defined above.default is 10 seconds)")]
            public float BetterFlyhackHoldTime { get; private set; } = 10;

            [JsonProperty("Enable Max Shooting Distance Check")]
            public bool EnableMaxShootingDistanceCheck { get; private set; } = true;
            [JsonProperty("Ignore Scientist (Max Distance Shots)")]
            public bool IgnoreScientist { get; private set; } = true;
            [JsonProperty("Max Shooting Distance (default fallback if no weapon is found upon attacking a player)")]
            public float MaxShootingDistance { get; private set; } = 285;

            [JsonProperty("Enable Shooting Height Check")]
            public bool EnableShootingHeightCheck { get; private set; } = true;

            [JsonProperty("Max Shooting Height")]
            public float MaxShootingHeight { get; private set; } = 3;

            [JsonProperty("Enable Discord Integration")]
            public bool EnableDiscordIntegration { get; private set; } = false;

            [JsonProperty("Discord Webhook URL")]
            public string DiscordWebhookUrl { get; private set; } = "Example - https://discord.com/api/webhooks/";

            [JsonProperty("Discord Webhook Cooldown (default: 30 seconds) - this is to simply prevent spam, set to '1' to disable.")]
            public float WebhookCooldown { get; private set; } = 30;

            [JsonProperty("Discord Username")]
            public string DiscordUsername { get; private set; } = "Anticheat+";

            [JsonProperty("Embed Color")]
            public int EmbedColor { get; private set; } = 16711680;

            [JsonProperty("Embed Title")]
            public string EmbedTitle { get; private set; } = "Anticheat Alert";

            [JsonProperty("Embed Footer")]
            public string EmbedFooter { get; private set; } = "Anticheat System";

            [JsonProperty("Flyhack Kick Reason")]
            public string FlyhackKickReason { get; private set; } = "Anticheat+ Violation [Flyhack]";

            [JsonProperty("Shoot in Air Kick Reason")]
            public string ShootInAirKickReason { get; private set; } = "Anticheat+ Violation [Shooting-in-air]";

            [JsonProperty("Shoot Max Distance Kick Reason")]
            public string ShootMaxDistanceKickReason { get; private set; } = "Anticheat+ Violation [Max-Shot-Distance]";
            [JsonProperty("Max Shooting Distance Per Weapon")]
            public Dictionary<string, (float, string)> MaxShootingDistancePerWeapon { get; set; } = new Dictionary<string, (float, string)>
            {
                { "rifle.ak", (300f, "https://rustlabs.com/img/items180/rifle.ak.png") },
                { "rifle.ak.diver", (300f, "https://rustlabs.com/img/items180/rifle.ak.png") },
                { "rifle.ak.ice", (300f, "https://rustlabs.com/img/items180/rifle.ak.png") },
                { "lmg.m249", (300f, "https://rustlabs.com/img/items180/lmg.m249.png") },
                { "hmlmg", (300f, "https://rustlabs.com/img/items180/hmlmg.png") },
                { "rifle.lr300", (300f, "https://rustlabs.com/img/items180/rifle.lr300.png") },
                { "rifle.bolt", (350f, "https://rustlabs.com/img/items180/rifle.bolt.png") },
                { "rifle.l96", (400f, "https://rustlabs.com/img/items180/rifle.l96.png") },
                { "rifle.semiauto", (200f, "https://rustlabs.com/img/items180/rifle.semiauto.png") },
                { "pistol.python", (100f, "https://rustlabs.com/img/items180/pistol.python.png") },
                { "pistol.semi", (100f, "https://rustlabs.com/img/items180/pistol.semiauto.png") },
                { "smg.mp5", (150f, "https://rustlabs.com/img/items180/smg.mp5.png") },
                { "smg.2", (120f, "https://rustlabs.com/img/items180/smg.2.png") },
                { "shotgun.pump", (40f, "https://rustlabs.com/img/items180/shotgun.pump.png") },
                { "shotgun.spas12", (30f, "https://rustlabs.com/img/items180/shotgun.spas12.png") },
                { "crossbow", (75f, "https://rustlabs.com/img/items180/crossbow.png") },
                { "bow.compound", (100f, "https://rustlabs.com/img/items180/bow.compound.png") },
                { "bow.hunting", (60f, "https://rustlabs.com/img/items180/bow.hunting.png") },
                { "shotgun.double", (40f, "https://rustlabs.com/img/items180/shotgun.double.png") },
                { "pistol.eoka", (10f, "https://rustlabs.com/img/items180/pistol.eoka.png") },
                { "flamethrower", (10f, "https://rustlabs.com/img/items180/flamethrower.png") },
                { "rifle.m249", (300f, "https://rustlabs.com/img/items180/lmg.m249.png") },
                { "rifle.m39", (250f, "https://rustlabs.com/img/items180/rifle.m39.png") },
                { "pistol.m92", (100f, "https://rustlabs.com/img/items180/pistol.m92.png") },
                { "smg.thompson", (150f, "https://rustlabs.com/img/items180/pistol.m92.png") },
                { "shotgun.waterpipe", (20f, "https://rustlabs.com/img/items180/shotgun.waterpipe.png") },
                { "shotgun.m4", (275f, "https://rustlabs.com/img/items180/shotgun.m4.png")},
            };
        }
        #endregion
        private Configuration _config;


        #region Hooks
        private void OnServerInitialized()
        {
            Puts("> Clearing existing data...");
            _checkPlayersTimer?.Destroy();

            Puts("> Loading configuration...");

            LoadConfig();
            LoadAdminPreferences();

            Puts("> Registering permissions...");
            permission.RegisterPermission("anticheatplus.bypass", this);
            permission.RegisterPermission("anticheatplus.admin", this);

            Puts("> Finding Glitchty Monuments (applying blocks)...");

            excavatorPosition = FindExcavatorCoordinates();


            Puts("> Starting Anticheat+ Core Modules...");

            _checkPlayersTimer = timer.Every(_config.AnticheatCheckSpeed, CheckPlayers);



            Puts($"> Anticheat+ Update Time: {_config.AnticheatCheckSpeed}s");
            Puts("> Anticheat+ is now running!");


        }

        private void OnPlayerInit(BasePlayer player)
        {
            playerPingHistory[player.userID.ToString()] = new List<int>();
        }

        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            string userIdString = player.userID.ToString();
            if (playerPingHistory.ContainsKey(userIdString))
            {
                playerPingHistory.Remove(userIdString);
            }
        }

        void Unload()
        {
            _checkPlayersTimer?.Destroy();
            _checkPlayersPingTimer?.Destroy();
            _checkServerStatusTimer?.Destroy();
        }


        void OnEntityTakeDamage(BaseCombatEntity entity, HitInfo info)
        {
            if (!(entity is BasePlayer) || info == null || info.Initiator == null || !(info.Initiator is BasePlayer))
                return;

            if (_config.IgnoreScientist && (entity is NPCPlayer || info.Initiator is NPCPlayer))
                return;

            BasePlayer attacker = info.Initiator.ToPlayer();
            BasePlayer victim = entity.ToPlayer();

            float distanceSquared = (attacker.transform.position - victim.transform.position).sqrMagnitude;
            bool isInTheAir = !attacker.modelState.onground;
            float heightAboveGround = attacker.transform.position.y - TerrainMeta.HeightMap.GetHeight(attacker.transform.position);
            bool isInVehicle = attacker.isMounted;

            Item weapon = attacker.GetActiveItem();
            if (weapon == null) return;
            string weaponShortname = weapon.info.shortname;
            float maxDistanceForWeapon = _config.MaxShootingDistancePerWeapon.ContainsKey(weaponShortname)
                                            ? _config.MaxShootingDistancePerWeapon[weaponShortname].Item1
                                            : _config.MaxShootingDistance;

            if (_config.EnableShootingHeightCheck && isInTheAir && !isInVehicle && !IsNearExcavator(attacker.transform.position))
            {
                if (heightAboveGround > _config.MaxShootingHeight)
                {
                    if (_config.debugMode) { Puts($"Shooting Height Violation: {attacker.displayName} - Height: {heightAboveGround}, Max Height: {_config.MaxShootingHeight}"); }
                    HandleViolation(attacker, distanceSquared, _config.ShootInAirKickReason, weapon);
                    AddViolation(cachedViolationsShootInAir, attacker);
                    return;
                }
            }

            if (_config.EnableMaxShootingDistanceCheck && distanceSquared > maxDistanceForWeapon * maxDistanceForWeapon)
            {
                if (_config.debugMode)
                { Puts($"Max Shooting Distance Violation: {attacker.displayName} - Distance: {Mathf.Sqrt(distanceSquared)}, Max Distance: {maxDistanceForWeapon}"); }
                HandleViolation(attacker, distanceSquared, _config.ShootMaxDistanceKickReason, weapon);
                AddViolation(cachedViolationsShootMaxDistance, attacker);
            }

        }


        #endregion

        #region Commands

        [ChatCommand("recentflag")]
        private void RecentFlagCommand(BasePlayer player, string command, string[] args)
        {
            if (!IsValidAdmin(player))
            {
                SendReply(player, "You do not have permission to use this command.");
                return;
            }

            if (lastFlaggedPlayer == null || !lastFlaggedPlayer.IsConnected)
            {
                SendReply(player, "No recent flags available or player is no longer online.");
                return;
            }

            player.Teleport(lastFlaggedPlayer.transform.position);
            SendReply(player, $"<color=white><size=20>Anticheat+</size></color>\n\n<color=white><size=16>Teleported to the most recent flagged player: {lastFlaggedPlayer.displayName}</size></color>");
        }


        [ChatCommand("togglenotifications")]
        private void ToggleAlertsCommand(BasePlayer player, string command, string[] args)
        {
            string steamId = player.UserIDString;
            if (!IsValidAdmin(player)) { return; }
            bool currentValue = AdminPreferences.ContainsKey(steamId) && AdminPreferences[steamId];
            AdminPreferences[steamId] = !currentValue;
            SaveAdminPreferences();
            SendReply(player, $"<color=white><size=20>Anticheat+</size></color>\n\n<color=white><size=16>In-game alerts are now {(AdminPreferences[steamId] ? "enabled" : "disabled")}.</size></color>");
        }
        #endregion

        #region Anticheat Core

        private void CheckPlayers()
        {
            if (_config.BetterFlyhack)
            {
                DetectFlyhack();
            }
            if (_config.AntiLagSwitch)
            {

                TrackPlayerPing();

            }
            if (_config.acThrottle)
            {

                CheckServerStatus();

            }
        }

        #endregion

        #region Dependencies 


        void HandleViolation(BasePlayer attacker, float distanceSquared, string reason, Item weapon)
        {
            lastFlaggedPlayer = attacker;
            int shootInAirCount = cachedViolationsShootInAir.TryGetValue(attacker, out ViolationCount inAirDetails) ? inAirDetails.Count : 0;
            int shootMaxDistanceCount = cachedViolationsShootMaxDistance.TryGetValue(attacker, out ViolationCount maxDistanceDetails) ? maxDistanceDetails.Count : 0;
            int flyhackCount = cachedViolationsFlyhackViolation.TryGetValue(attacker, out ViolationCount flyhackDetails) ? flyhackDetails.Count : 0;

            string discordMessage = $"- **Player:** `{attacker.displayName}`\n" +
                                    $"- **Steam ID:** [{attacker.UserIDString}](https://steamcommunity.com/profiles/{attacker.UserIDString})\n" +
                                    $"- **Reason:** `{reason}`\n" +
                                    $"- **Position:** `{attacker.transform.position}`\n" +
                                    $"- **Time:** <t:{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}> (<t:{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}:R>)\n" +
                                    $"- **Server:** `{ConVar.Server.hostname}`\n" +
                                    $"- **Violations Summary:**\n" +
                                    $"    - Shooting in Air: {shootInAirCount}\n" +
                                    $"    - Max Distance Shots: {shootMaxDistanceCount}\n" +
                                    $"    - Flyhack Attempts: {flyhackCount}";
            string imageLink = null;

            if (reason == _config.ShootMaxDistanceKickReason && weapon != null)
            {
                if (_config.MaxShootingDistancePerWeapon.TryGetValue(weapon.info.shortname, out var weaponData))
                {
                    imageLink = weaponData.Item2;
                }

                string weaponInfo = $"- **Weapon Type:** `{weapon.info.shortname}`\n- **Distance:** `{Mathf.Sqrt(distanceSquared)}m`\n";
                discordMessage = weaponInfo + discordMessage;
            }

            if (!_config.FlagAdmins && HasPermission(attacker)) { return; }

            if (_config.KickUponFlag)
            {
                attacker.Kick(reason);
                discordMessage = "- **Player Kicked:** " + discordMessage.Substring(3);
            }

            if (_config.EnableDiscordIntegration)
            {
                SendDiscordMessage(discordMessage, imageLink);
            }

            if (_config.AlertIngameAdmins)
            {
                string message = $"{attacker.displayName} has been flagged for: {reason} /recentflag to teleport to the player";

                foreach (BasePlayer player in BasePlayer.activePlayerList)
                {
                    if (IsValidAdmin(player) && ShouldReceiveAlerts(player))
                    {
                        player.SendConsoleCommand($"gametip.showgametip \"{message}\"");
                        timer.Once(2.0f, () => { player.SendConsoleCommand("gametip.hidegametip"); });
                    }
                }
            }
        }

        bool timersEnabled = true;

        void CheckServerStatus()
        {
            int currentFPS = Performance.report.frameRate;

            if (_config.debugMode)
            {
                Puts($"> Current FPS: {currentFPS}");

            }
            if (currentFPS < 13)
            {
                if (timersEnabled)
                {
                    timer.Destroy(ref _checkPlayersTimer);
                    timer.Destroy(ref _checkPlayersPingTimer);
                    timersEnabled = false;

                    Puts("> Server FPS has fell below 13 FPS - Anticheat+ has disabled some features to prevent server lag.");
                }
            }
            else if (currentFPS > 15 && !timersEnabled)
            {
                _checkPlayersTimer = timer.Every(_config.AnticheatCheckSpeed, CheckPlayers);
                _checkPlayersPingTimer = timer.Every(_config.AnticheatCheckSpeed, TrackPlayerPing);
                timersEnabled = true;
                Puts("> Server FPS has recovered - Anticheat+ has re-enabled all features.");
            }

        }


        private void DetectFlyhack()
        {
            foreach (BasePlayer player in BasePlayer.activePlayerList)
            {
                if (!player.IsConnected || HasPermission(player) || IsNearExcavator(player.transform.position)) continue;
                float heightAboveGround = player.transform.position.y - TerrainMeta.HeightMap.GetHeight(player.transform.position);
                bool isInTheAir = !player.modelState.onground && !player.isMounted && !player.IsSwimming();
                if (_config.debugMode)
                {
                    Puts($"Player - {player.displayName} (isInTheAir: {isInTheAir}, isSwimming: {player.IsSwimming()}) - current height: {heightAboveGround}");
                }
                if (isInTheAir && heightAboveGround > _config.BetterFlyhackThreshold)
                {
                    if (!elevatedPlayers.ContainsKey(player.userID))
                    {
                        elevatedPlayers[player.userID] = DateTime.UtcNow;
                    }
                    else if (DateTime.UtcNow - elevatedPlayers[player.userID] > TimeSpan.FromSeconds(_config.BetterFlyhackHoldTime))
                    {
                        HandleViolation(player, heightAboveGround, _config.FlyhackKickReason, null);
                        AddViolation(cachedViolationsFlyhackViolation, player);
                        elevatedPlayers.Remove(player.userID);
                    }
                }
                else if (elevatedPlayers.ContainsKey(player.userID))
                {
                    elevatedPlayers.Remove(player.userID);
                }
            }
        }

        private void TrackPlayerPing()
        {
            {
                foreach (BasePlayer basePlayer in BasePlayer.activePlayerList)
                {
                    IPlayer player = covalence.Players.FindPlayerById(basePlayer.UserIDString);

                    if (player != null)
                    {
                        int currentPing = player.Ping;

                        if (_config.debugMode)
                        {
                            Puts($"{player.Name} - {currentPing}ms");
                        }

                        if (!playerPingHistory.TryGetValue(player.Id, out List<int> history))
                        {
                            history = new List<int>();
                            playerPingHistory.Add(player.Id, history);
                        }

                        history.Add(currentPing);
                        if (history.Count > 60) history.RemoveAt(0);
                        if (IsPingSuspicious(history))
                        {
                            string discordMessage = $"- **Player:** `{player.Name}`\n" +
                                                    $"- **Steam ID:** [{player.Id}](https://steamcommunity.com/profiles/{player.Id})\n" +
                                                    $"- **Reason:** `Lagswitching Detected! `\n" +
                                                    $"- **Position:** `{player.Position()}`\n" +
                                                    $"- **Time:** <t:{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}> (<t:{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}:R>)\n" +
                                                    $"- **Server:** `{ConVar.Server.hostname}`\n" +
                                                    $"- **Ping Data:**\n" +
                                                    $"    - Current Ping: {player.Ping}ms\n" +
                                                    $"    - Last 5 Pings: {string.Join(", ", history.Skip(Math.Max(0, history.Count - 5)))}";

                            SendDiscordMessage(discordMessage, null);
                        }
                    }
                }
            }
        }



        private bool IsPingSuspicious(List<int> history)
        {
            if (history.Count < 10) return false;

            int latestPing = history[history.Count - 1];
            int averagePing = 0;
            history.ForEach(p => averagePing += p);
            averagePing /= history.Count;

            if (latestPing > averagePing * 3 && latestPing > 350)
            {
                return true;
            }
            return false;
        }


        private bool ShouldReceiveAlerts(BasePlayer player)
        {
            return AdminPreferences.TryGetValue(player.UserIDString, out bool receiveAlerts) && receiveAlerts;
        }

        private void SendDiscordMessage(string content, string imageLink = null)
        {
            if (DateTime.UtcNow - lastMessageSent < messageCooldown)
            {
                return;
            }

            var embed = new
            {
                title = _config.EmbedTitle,
                description = content,
                color = _config.EmbedColor,
                footer = new { text = _config.EmbedFooter },
                thumbnail = imageLink != null ? new { url = imageLink } : null
            };

            var payload = new
            {
                username = _config.DiscordUsername,
                embeds = new[] { embed }
            };

            string jsonPayload = Newtonsoft.Json.JsonConvert.SerializeObject(payload);

            webrequest.Enqueue(_config.DiscordWebhookUrl, jsonPayload, (code, response) =>
            {
                if (code != 200 && code != 204)
                {
                    Puts($"Error: Failed to send Discord webhook.Status code: {code}");
                }
                else
                {
                    lastMessageSent = DateTime.UtcNow;
                }
            }, this, RequestMethod.POST, new Dictionary<string, string> { { "Content-Type", "application/json" } });
        }

        private Vector3 FindExcavatorCoordinates()
        {
            foreach (var entity in BaseNetworkable.serverEntities)
            {
                if (entity?.ShortPrefabName != null && entity.ShortPrefabName.Contains("excavator"))
                {
                    return entity.transform.position;
                }
            }
            return Vector3.zero;

        }
        private bool IsNearExcavator(Vector3 playerPosition)
        {
            float radius = 100f;
            return Vector3.Distance(playerPosition, excavatorPosition) < radius;
        }

        private bool IsValidAdmin(BasePlayer player)
        {
            return permission.UserHasPermission(player.UserIDString, "anticheatplus.admin");
        }

        private bool HasPermission(BasePlayer player)
        {
            return permission.UserHasPermission(player.UserIDString, "anticheatplus.bypass");

        }
        private void AddViolation(Dictionary<BasePlayer, ViolationCount> cache, BasePlayer player)
        {
            if (cache.TryGetValue(player, out ViolationCount existingViolation))
            {
                existingViolation.Count++;
                existingViolation.TimeOfLastViolation = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
            }
            else
            {
                cache[player] = new ViolationCount
                {
                    Count = 1,
                    TimeOfLastViolation = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
                };
            }
        }



        #endregion
    }
}

Автор
15FPS
Скачиваний
0
Просмотры
1
Первый выпуск
Обновление
Оценка
0.00 звёзд 0 оценок
Сверху