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

FREE ProximitySystem : 1.0.8 1.0.8

Нет прав для скачивания
C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Oxide.Core;
using System;
using Oxide.Core.Plugins;
using System.Diagnostics;
using ConVar;
using Physics = UnityEngine.Physics;
using Network;
using ProtoBuf;

namespace Oxide.Plugins
{
    [Info("ProximitySystem", "Visagalis", "1.0.8")]
    [Description("Announces to DiscordMessages if players are wandering around in bigger groups.")]
    class ProximitySystem : RustPlugin
    {
        private int trackTimer;
        private int trackMeters;
        private int announceThreshold;
        private int banThreshold;
        private int kickThreshold;
        private int groupSize;
        private int keepHistoryForMinutes;
        private bool UseDiscordMessages;
		private bool ClearOnKill;
        private string discordMessagesWebhookUrl;
        private bool AddHereTag;

        private Dictionary<string, List<DateTime>> proximityHistory = new Dictionary<string, List<DateTime>>();

        [PluginReference("DiscordMessages")]
        Plugin discordMessages;

        public class ProximityData
        {
            public ulong playerA;
            public ulong playerB;
            public int range;
            public DateTime date;
        }

        void LoadConfig()
        {
            SetConfig("Settings", "Run check every x seconds", "60"); // trackTimer
            SetConfig("Settings", "Investigate players who are around x meters", "60"); // trackMeters
            SetConfig("Settings", "Announce threshold", "5"); //announceThreshold
            SetConfig("Settings", "Ban threshold", "0"); //banThreshold
            SetConfig("Settings", "Kick threshold", "0"); //kickThreshold
            SetConfig("Settings", "Group size", "4"); // groupSize
            SetConfig("Settings", "Clear data on kill", "1"); // ClearOnKill
            SetConfig("Settings", "Use DiscordMessages", "0"); // UseDiscordMessages
            SetConfig("Settings", "DiscordMessages WebhookURL", ""); // discordMessagesWebhookUrl
            SetConfig("Settings", "Keep history for x minutes", "60"); // keepHistoryForMinutes
            SetConfig("Settings", "Add @here tag", "1"); // AddHereTag
        }

        void InitConfig()
        {
            trackTimer = int.Parse(Config["Settings", "Run check every x seconds"].ToString());
            trackMeters = int.Parse(Config["Settings", "Investigate players who are around x meters"].ToString());
            announceThreshold = int.Parse(Config["Settings", "Announce threshold"].ToString());
            banThreshold = int.Parse(Config["Settings", "Ban threshold"].ToString());
            kickThreshold = int.Parse(Config["Settings", "Kick threshold"].ToString());
            groupSize = int.Parse(Config["Settings", "Group size"].ToString());
			ClearOnKill = int.Parse(Config["Settings", "Clear data on kill"].ToString()) == 1;
            UseDiscordMessages = int.Parse(Config["Settings", "Use DiscordMessages"].ToString()) == 1;
            discordMessagesWebhookUrl =  Config["Settings", "DiscordMessages WebhookURL"].ToString();
            keepHistoryForMinutes = int.Parse(Config["Settings", "Keep history for x minutes"].ToString());
            AddHereTag = int.Parse(Config["Settings", "Add @here tag"].ToString()) == 1;
        }

        void LoadDefaultConfig()
        {
            Puts("Generating new config file...");
            LoadConfig();
        }

        void SetConfig(params object[] args)
        {
            List<string> stringArgs = (from arg in args select arg.ToString()).ToList();
            stringArgs.RemoveAt(args.Length - 1);

            if (Config.Get(stringArgs.ToArray()) == null) Config.Set(args);
        }

		void OnPlayerDeath(BasePlayer player, HitInfo info)
		{
			if (info?.InitiatorPlayer != null)
			{
				var playerA = Math.Min(player.userID, info.InitiatorPlayer.userID);
				var playerB = Math.Max(player.userID, info.InitiatorPlayer.userID);
				List<string> keysToRemove = new List<string>();
				foreach (var key in proximityHistory.Keys)
				{
					if (key.Contains(playerA.ToString()) && key.Contains(playerB.ToString()))
					   keysToRemove.Add(key);
				}

				foreach (var key in keysToRemove)
				{
					if (proximityHistory.ContainsKey(key))
						proximityHistory.Remove(key);
				}
			}
		}

        void OnServerInitialized()
        {
            LoadConfig();
            InitConfig();
			timer.Once(trackTimer, () =>
			{
				if(ClearOnKill)
                    Subscribe(nameof(OnPlayerDeath));
				else
                    Unsubscribe(nameof(OnPlayerDeath));
				
				RefreshData();
			});
        }

        void RefreshData()
        {
            Stopwatch perf = new Stopwatch();
            perf.Start();
            List<ProximityData> dataList = new List<ProximityData>();
            foreach (var currentPlayer in BasePlayer.activePlayerList)
            {
                if (dataList.Any(p => p.playerA == currentPlayer.userID))
                    continue;
                foreach (var distantPlayer in BasePlayer.activePlayerList)
                {
                    if (distantPlayer.IsAdmin || currentPlayer.IsAdmin)
                        continue;

                    if (distantPlayer.InSafeZone() || currentPlayer.InSafeZone())
                        continue;

                    if (distantPlayer.GetComponentInParent<CargoShip>() != null || currentPlayer.GetComponentInParent<CargoShip>() != null)
                        continue;

                    if (distantPlayer.IsWounded() || currentPlayer.IsWounded()
					    || distantPlayer.IsDead() || currentPlayer.IsDead())
                        continue;

                    if (currentPlayer.userID == distantPlayer.userID // same user
                        || dataList.Exists(d =>
                            d.playerA == Math.Min(currentPlayer.userID, distantPlayer.userID) 
                            && 
                            d.playerB == Math.Max(currentPlayer.userID, distantPlayer.userID))) // already in list
                        continue;


                    var distance = Vector3.Distance(currentPlayer.eyes.position, distantPlayer.eyes.position);
                    if (distance <= trackMeters && distantPlayer.IsAlive())
                    {
                        RaycastHit hitInfo;
                        if (!Physics.Linecast(currentPlayer.eyes.position, distantPlayer.eyes.position, out hitInfo,
                            LayerMask.GetMask("Player (Server)", "Construction", "Deployed", "World", "Default")))
                            continue;

                        if (hitInfo.collider.name.Contains("player.prefab"))
                            dataList.Add(new ProximityData
                            {
                                playerA = Math.Min(currentPlayer.userID, distantPlayer.userID),
                                playerB = Math.Max(currentPlayer.userID, distantPlayer.userID),
                                range = (int) distance
                            });
                    }
                }
            }

            Dictionary<ulong, ulong[]> playersInBiggerGroups = new Dictionary<ulong, ulong[]>();
            foreach(var playerId in BasePlayer.activePlayerList.Select(p => p.userID))
            {
                int playersInGroup = dataList.Count(d => d.playerA == playerId || d.playerB == playerId) + 1;
                if (playersInGroup >= groupSize)
                {
                    if (playersInBiggerGroups.All(d => d.Key != playerId && !d.Value.Contains(playerId))) {
                        var playersWhoAreClose = dataList.Where(d => d.playerB == playerId).Select(p => p.playerA).Concat(dataList.Where(d => d.playerA == playerId).Select(p => p.playerB));
                        playersInBiggerGroups.Add(playerId, playersWhoAreClose.ToArray());
                    }
                }
            }

            DateTime currTime = DateTime.Now;
            foreach (var playerId in playersInBiggerGroups.Keys.OrderBy(p => p))
            {
                BasePlayer currPlr = BasePlayer.FindByID(playerId);
                string playerNames = $"http://steamcommunity.com/profiles/{currPlr.userID} | {currPlr.displayName}";
                var orderedList =  playersInBiggerGroups[playerId].OrderBy(p => p).ToArray();
                var keyForHistory = playerId + "-" + string.Join("-", orderedList.Select(u => u.ToString()).ToArray());
                if (!proximityHistory.ContainsKey(keyForHistory))
                    proximityHistory.Add(keyForHistory, new List<DateTime>());
                proximityHistory[keyForHistory].Add(currTime);
                int minTimesFound = 99999;
                foreach (var newPlayer in orderedList)
                {
                    var otherPlayer = BasePlayer.FindByID(newPlayer);
                    int timesFound = TimesFoundTogether(playerId, newPlayer);
                    if (minTimesFound > timesFound)
                        minTimesFound = timesFound;
                    playerNames += $"\nhttp://steamcommunity.com/profiles/{otherPlayer.userID} | {otherPlayer.displayName} ({timesFound})";
                }

                if(minTimesFound % announceThreshold == 0) 
                {
                    Puts($"Found players who are in group of *{playersInBiggerGroups[playerId].Length + 1}*:\n{playerNames}");                    
                    AnnounceGroup(playerId, playersInBiggerGroups[playerId]);
                    if (AddHereTag)
                        AnnounceToExternal("@here");
                }

                if (banThreshold > 0 && minTimesFound >= banThreshold)
                {
                    AnnounceToExternal($"Banned group of *{playersInBiggerGroups[playerId].Length + 1}*:\n{playerNames}");
                    BanGroup(playerId, playersInBiggerGroups[playerId]);
                }

                if (kickThreshold > 0 && minTimesFound >= kickThreshold)
                {
                    AnnounceToExternal($"Kicked group of *{playersInBiggerGroups[playerId].Length + 1}*:\n{playerNames}");
                    KickGroup(playerId, playersInBiggerGroups[playerId]);
                }
            }

            perf.Stop();
			if(perf.ElapsedMilliseconds > 500) // only log if performance is bad.timer.Once(1 , () => AnnounceToExternal($"Found {playersInBiggerGroups.Count} groups which are equal or bigger than {groupSize} took {perf.ElapsedMilliseconds}ms!"));

            timer.Once(trackTimer, RefreshData);
        }

        private void BanGroup(ulong playerId, ulong[] playersInGroup)
        {
            var leadPlayer = BasePlayer.FindByID(playerId);
            Ban(leadPlayer, $"Groups bigger than {groupSize-1} are automatically banned.");
            foreach (var groupPlayerId in playersInGroup)
            {
                var groupPlayer = BasePlayer.FindByID(groupPlayerId);
                Ban(groupPlayer, $"Groups bigger than {groupSize - 1} are automatically banned.({leadPlayer.displayName})");
            }
        }

        private void KickGroup(ulong playerId, ulong[] playersInGroup)
        {
            var leadPlayer = BasePlayer.FindByID(playerId);
            Kick(leadPlayer, $"Groups bigger than {groupSize - 1} are automatically kicked.");
            foreach (var groupPlayerId in playersInGroup)
            {
                var groupPlayer = BasePlayer.FindByID(groupPlayerId);
                Kick(groupPlayer, $"Groups bigger than {groupSize - 1} are automatically kicked.({leadPlayer.displayName})");
            }
        }

        private void Ban(BasePlayer player, string reason)
        {
            ServerUsers.User user = ServerUsers.Get(player.userID);
            if (user != null && user.group == ServerUsers.UserGroup.Banned)
            {
                Puts($"Failed to ban player: {player.userID}.");
            }
            else
            {
                ServerUsers.Set(player.userID, ServerUsers.UserGroup.Banned, player.displayName, reason);
                string str = "";
                if (player.IsConnected && (long) player.net.connection.ownerid != (long) player.net.connection.userid)
                {
                    str = str + " and also banned ownerid " + (object) player.net.connection.ownerid;
                    ServerUsers.Set(player.net.connection.ownerid, ServerUsers.UserGroup.Banned, player.displayName,
                        $"Family share owner of {player.net.connection.userid}");
                }

                ServerUsers.Save();
                Puts("Kickbanned User: " + player.userID + " - " + player.displayName + str);
                Network.Net.sv.Kick(player.net.connection, "Banned: " + reason);
            }
        }

        private void Kick(BasePlayer player, string reason)
        {
            player.Kick(reason);
        }

        private int TimesFoundTogether(ulong player, ulong otherPlayer)
        {
            ClearOldEntries();
            return proximityHistory.Where(p => p.Key.Contains(player.ToString()) && p.Key.Contains(otherPlayer.ToString())).Sum(p=> p.Value.Count);
        }

        private void ClearOldEntries()
        {
            foreach (var key in proximityHistory.Keys)
            {
                proximityHistory[key].RemoveAll(p => p < DateTime.Now.AddMinutes(keepHistoryForMinutes * -1));
            }

            foreach (var key in proximityHistory.Where(p => p.Value.Count == 0).Select(k => k.Key).ToArray())
            {
                proximityHistory.Remove(key);
            }
        }

        private void AnnounceToExternal(string message)
        {
            Puts(message);
            if (UseDiscordMessages)
                discordMessages?.Call("API_SendTextMessage", discordMessagesWebhookUrl, message);
        }

        private void AnnounceGroup(ulong playerId, ulong[] list)
        {
            if (!UseDiscordMessages)
                return;

            BasePlayer player = BasePlayer.FindByID(playerId);
            var players = list.Select(x => new { p = BasePlayer.FindByID(x), foundTogether = TimesFoundTogether(playerId, x) }).ToArray();
            var playersData = $"[{player?.displayName}](https://steamcommunity.com/profiles/{player?.userID})\n";

            foreach(var plr in players)
            {
                playersData += $"[{plr.p?.displayName}](https://steamcommunity.com/profiles/{plr.p?.userID}) ({plr.foundTogether})\n";
            }

            object payload = new List<dynamic>()
            {
                new{name="Players", value=playersData.Trim(), inline = false },
                new{name="Location", value=GetPositionNameInGrid(player.transform.position), inline = false },
            };

            string json = JsonConvert.SerializeObject(payload);
            discordMessages?.Call("API_SendFancyMessage", discordMessagesWebhookUrl, $"Group of {list.Length + 1} detected", 3329330, json);
        }


        string GetPositionNameInGrid(Vector3 position) // Credit: Jake_Rich
        {
            Vector2 roundedPos = new Vector2(World.Size / 2 + position.x, World.Size / 2 - position.z);

            string grid = $"{NumberToLetter((int)(roundedPos.x / 150))}{(int)(roundedPos.y / 150)} {position}";

            return grid;
        }

        public static string NumberToLetter(int num) // Credit: Jake_Rich
        {
            int num2 = Mathf.FloorToInt((float)(num / 26));
            int num3 = num % 26;
            string text = string.Empty;
            if (num2 > 0)
            {
                for (int i = 0; i < num2; i++)
                {
                    text += Convert.ToChar(65 + i);
                }
            }
            return text + Convert.ToChar(65 + num3);
        }

        [ConsoleCommand("testprox")]
        private void ccmdTestProx(ConsoleSystem.Arg arg)
        {
            if (!arg.IsAdmin)
                return;

            if (BasePlayer.activePlayerList.Any()) 
            { 
                var testUserId = BasePlayer.activePlayerList.First().userID;
                AnnounceGroup(testUserId, new ulong[] { testUserId, testUserId, testUserId });
            }
            AnnounceToExternal("Testing ProximitySystem announcement work.");
        }
    }
}

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