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

FREE FCleaner : 2.3.0 2.3.0

Нет прав для скачивания
C#:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Plugins;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("FCleaner", "kamAp", "2.3.0")]
    public class FCleaner : RustPlugin
    {
        public class Configuration
        {
            [JsonProperty("Префикс сообщения")]
            public string Prefix { get; set; } = "[FCleaner]";

            [JsonProperty("Цвет префикса")]
            public string PrefixColor { get; set; } = "#34eb4f";

            [JsonProperty("Интервал очистки (в секундах)")]
            public float CleanInterval { get; set; } = 200f;

            [JsonProperty("Очищать выброшенные предметы")]
            public bool CleanDroppedItems { get; set; } = true;

            [JsonProperty("Очищать трупы игроков")]
            public bool CleanPlayerCorpses { get; set; } = true;

            [JsonProperty("Очищать рюкзаки NPC")]
            public bool CleanNPCBackpacks { get; set; } = true;

            [JsonProperty("Очищать рюкзаки из предметов")]
            public bool CleanDropBackpacks { get; set; } = true;

            [JsonProperty("Очищать трупы с лутом")]
            public bool CleanLootableCorpses { get; set; } = true;

            [JsonProperty("Очищать неактивные сущности")]
            public bool CleanInactiveEntities { get; set; } = true;

            [JsonProperty("Время уведомления перед очисткой (сек)")]
            public float CleanNotificationTime { get; set; } = 30f;

            [JsonProperty("Список исключённых предметов")]
            public List<string> ExcludedItems { get; set; } = new List<string>();

            [JsonProperty("Автоматически добавлять карточки в исключённые предметы")]
            public bool AutoAddKeycardsToExcluded { get; set; } = false;

            [JsonProperty("Включить бан за спам выбрасыванием предметов")]
            public bool BanEnabled { get; set; } = true;

            [JsonProperty("Порог предупреждения при выбросе")]
            public int ItemDropWarningThreshold { get; set; } = 30;

            [JsonProperty("Порог бана при выбросе")]
            public int ItemDropBanThreshold { get; set; } = 50;

            [JsonProperty("Время сброса счётчика (сек)")]
            public float ItemDropResetTime { get; set; } = 60f;

            [JsonProperty("Длительность бана (в минутах)")]
            public int BanDuration { get; set; } = 60;

            [JsonProperty("Сообщение предупреждения при выбросе")]
            public string WarningMessage { get; set; } = "Внимание: Вы выбросили {0} предметов. Вас забанят, если вы продолжите.";

            [JsonProperty("Причина бана")]
            public string BanReason { get; set; } = "Слишком много выброшенных предметов за короткий срок.";

            [JsonProperty("Разрешение администратора")]
            public string AdminPermission { get; set; } = "fcleaner.admin";

            [JsonProperty("Разрешение на уведомление")]
            public string NotifyPermission { get; set; } = "fcleaner.notify";

            [JsonProperty("Показывать метки на карте")]
            public bool ShowMarkersOnMap { get; set; } = true;

            [JsonProperty("Тип маркера на карте (только vending поддерживается)")]
            public string MapMarkerType { get; set; } = "vending";

            [JsonProperty("Название маркера на карте")]
            public string MapMarkerName { get; set; } = "Зона Очистки";

            [JsonProperty("Включить уведомления об очистке (в чат)")]
            public bool EnableCleanupMessages { get; set; } = true;

            [JsonProperty("Включить уведомления о выбросе (бан/предупреждения)")]
            public bool EnableDropWarnings { get; set; } = true;

            [JsonProperty("Отключить коллизии у предметов")]
            public bool DisableItemCollisions { get; set; } = false;

            [JsonProperty("Логировать изменение слоёв предметов")]
            public bool LogLayerChanges { get; set; } = false;

            [JsonProperty("Включить авто-очистку по типам предметов")]
            public bool AutoCleanByTypeEnabled { get; set; } = false;

            [JsonProperty("Порог авто-очистки по типам предметов")]
            public int AutoCleanTypeThreshold { get; set; } = 100;

            [JsonProperty("Включить пошаговую очистку")]
            public bool EnablePartialCleanup { get; set; } = true;

            [JsonProperty("Размер пакета очистки")]
            public int CleanupChunkSize { get; set; } = 50;

            [JsonProperty("Задержка между пакетами очистки (сек)")]
            public float CleanupChunkDelay { get; set; } = 0.5f;

            [JsonProperty("Кастомная команда для бана")]
            public string CustomBanCommand { get; set; } = "banid";

            [JsonProperty("Кастомные предметы для очистки на нулевых координатах")]
            public List<string> CustomZeroPointItems { get; set; } = new List<string>
            {
                "boat",
                "tugboat",
                "rhib"
            };

            [JsonProperty("Использовать расширенную систему бана (IP/Steam)")]
            public bool UseExtendedBanSystem { get; set; } = false;

            [JsonProperty("Тип расширенного бана (IP/Steam)")]
            public string ExtendedBanType { get; set; } = "IP";
        }

        private Configuration config;
        private Dictionary<ulong, bool> playerDroppedItems = new Dictionary<ulong, bool>();

        protected override void LoadDefaultConfig()
        {
            config = new Configuration();
            SaveConfig();
        }

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>();
                if (config == null) throw new Exception();
            }
            catch
            {
                PrintWarning("Ошибка загрузки конфигурации. Восстанавливаем стандартные настройки...");
                LoadDefaultConfig();
            }
            config.ExcludedItems = config.ExcludedItems.Distinct().ToList();
            config.CustomZeroPointItems = config.CustomZeroPointItems.Distinct().ToList();
            SaveConfig();
        }

        protected override void SaveConfig() => Config.WriteObject(config, true);

        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["CleaningInterval"] = "Очистка каждые {0} секунд.",
                ["TotalCleaned"] = "Удалено объектов: ({0}) всего, ({1}) выброшенных предметов, ({2}) трупов игроков, ({3}) рюкзаков NPC, ({4}) рюкзаков с предметами, ({5}) добычных трупов, ({6}) неактивных сущностей.",
                ["CleaningNotification"] = "Очистка начнется через {0} секунд в отмеченных местах.",
                ["DropWarning"] = "Внимание: Вы выбросили {0} предметов. Вас забанят, если вы продолжите.",
                ["DropBan"] = "Игрок {0} был забанен по причине: {1}",
                ["AdminNotify"] = "Игрок {0} ({1}) близок к бану за выброс предметов.",
                ["ForceCleanTriggered"] = "Принудительная очистка запущена!"
            }, this, "ru");

            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["CleaningInterval"] = "Cleanup every {0} seconds.",
                ["TotalCleaned"] = "Removed objects: ({0}) total, ({1}) dropped items, ({2}) player corpses, ({3}) NPC backpacks, ({4}) item backpacks, ({5}) lootable corpses, ({6}) inactive entities.",
                ["CleaningNotification"] = "Cleanup will start in {0} seconds in marked areas.",
                ["DropWarning"] = "Warning: You have dropped {0} items. You will be banned if you continue.",
                ["DropBan"] = "Player {0} was banned for reason: {1}",
                ["AdminNotify"] = "Player {0} ({1}) is close to a ban for item dropping.",
                ["ForceCleanTriggered"] = "Forced cleanup started!"
            }, this, "en");

            lang.RegisterMessages(new Dictionary<string, string>
            {
                ["CleaningInterval"] = "Nettoyage toutes les {0} secondes.",
                ["TotalCleaned"] = "Objets supprimés : ({0}) au total, ({1}) objets jetés, ({2}) cadavres de joueurs, ({3}) sacs à dos PNJ, ({4}) sacs à dos d'objets, ({5}) cadavres ouvrables, ({6}) entités inactives.",
                ["CleaningNotification"] = "Le nettoyage commencera dans {0} secondes dans les zones marquées.",
                ["DropWarning"] = "Attention : vous avez jeté {0} objets. Vous serez banni si vous continuez.",
                ["DropBan"] = "Le joueur {0} a été banni pour la raison : {1}",
                ["AdminNotify"] = "Le joueur {0} ({1}) est proche d'un bannissement pour avoir jeté des objets.",
                ["ForceCleanTriggered"] = "Nettoyage forcé lancé !"
            }, this, "fr");
        }

        public interface ICleanerModule
        {
            void Initialize();
            void OnServerInitialized();
            void Unload();
            void OnEntitySpawned(BaseNetworkable entity);
            void OnItemAction(Item item, string action, BasePlayer player);
            void ForceCleanup();
        }

        public void BroadcastToAll(string message)
        {
            foreach (var player in BasePlayer.activePlayerList)
                player.ChatMessage(message);
        }

        public void RunServerCommand(string command)
        {
            ConsoleSystem.Run(ConsoleSystem.Option.Server, command);
        }

        private bool IsJetpackItem(DroppedItem droppedItem)
        {
            return droppedItem != null && droppedItem.item != null && 
                   droppedItem.item.info.shortname == "largebackpack" && 
                   droppedItem.children.Any(x => x is BaseMountable);
        }

        private bool IsEntityJetpack(BaseNetworkable entity)
        {
            if (entity is DroppedItem droppedItem)
            {
                return IsJetpackItem(droppedItem);
            }
            return false;
        }

        public class CollisionModule : ICleanerModule
        {
            private FCleaner plugin;
            public CollisionModule(FCleaner plugin) { this.plugin = plugin; }
            public void Initialize() {}
            public void OnServerInitialized()
            {
                if (!plugin.config.DisableItemCollisions) return;
                Physics.IgnoreLayerCollision(17, 17, true);
                Physics.IgnoreLayerCollision(17, 21, false);
                Physics.IgnoreLayerCollision(17, 8, false);
                Physics.IgnoreLayerCollision(17, 0, false);
                Physics.IgnoreLayerCollision(29, 29, true);
                Physics.IgnoreLayerCollision(29, 21, false);
                Physics.IgnoreLayerCollision(29, 8, false);
                Physics.IgnoreLayerCollision(29, 0, false);
            }
            public void Unload() {}
            public void OnEntitySpawned(BaseNetworkable entity)
            {
                if (!plugin.config.DisableItemCollisions) return;
                if (entity is DroppedItem di)
                {
                    int oldLayer = di.gameObject.layer;
                    di.gameObject.layer = 17;
                    if (plugin.config.LogLayerChanges)
                        plugin.Puts($"[FCleaner] DroppedItem '{di.ShortPrefabName}' layer {oldLayer} -> 17");
                }
            }
            public void OnItemAction(Item item, string action, BasePlayer player) {}
            public void ForceCleanup() {}
        }

        public class AutoCleanByTypeModule : ICleanerModule
        {
            private FCleaner plugin;
            private Dictionary<string, int> droppedItemCountByType = new Dictionary<string, int>();
            public AutoCleanByTypeModule(FCleaner plugin) { this.plugin = plugin; }
            public void Initialize() {}
            public void OnServerInitialized() {}
            public void Unload() { droppedItemCountByType.Clear(); }
            public void OnEntitySpawned(BaseNetworkable entity)
            {
                if (!plugin.config.AutoCleanByTypeEnabled || plugin.config.AutoCleanTypeThreshold <= 0) return;
                if (entity is DroppedItem droppedItem)
                {
                    if (plugin.IsJetpackItem(droppedItem)) return;

                    var shortName = droppedItem.item?.info?.shortname;
                    if (string.IsNullOrEmpty(shortName)) return;
                    if (plugin.config.ExcludedItems.Contains(shortName)) return;
                    if (!droppedItemCountByType.ContainsKey(shortName))
                        droppedItemCountByType[shortName] = 0;
                    droppedItemCountByType[shortName]++;
                    if (droppedItemCountByType[shortName] >= plugin.config.AutoCleanTypeThreshold)
                    {
                        var allDropped = UnityEngine.Object.FindObjectsOfType<DroppedItem>()
                            .Where(d => d?.item?.info?.shortname == shortName).ToList();
                        if (allDropped.Count > 0)
                        {
                            foreach (var di in allDropped)
                                di?.Kill();
                            plugin.Puts($"Авто-очистка по типу '{shortName}' (удалено {allDropped.Count} шт.) Порог: {plugin.config.AutoCleanTypeThreshold}");
                        }
                        droppedItemCountByType[shortName] = 0;
                    }
                }
            }
            public void OnItemAction(Item item, string action, BasePlayer player) {}
            public void ForceCleanup()
            {
                if (!plugin.config.AutoCleanByTypeEnabled || plugin.config.AutoCleanTypeThreshold <= 0) return;
                var items = UnityEngine.Object.FindObjectsOfType<DroppedItem>();
                var itemCount = new Dictionary<string, int>();
                foreach (var i in items)
                {
                    if (i?.item?.info == null) continue;
                    if (plugin.IsJetpackItem(i)) continue;

                    var shortName = i.item.info.shortname;
                    if (plugin.config.ExcludedItems.Contains(shortName)) continue;
                    if (!itemCount.ContainsKey(shortName))
                        itemCount[shortName] = 0;
                    itemCount[shortName]++;
                }
                foreach (var kvp in itemCount)
                {
                    if (kvp.Value >= plugin.config.AutoCleanTypeThreshold)
                    {
                        var toRemove = items.Where(x => x?.item?.info?.shortname == kvp.Key).ToList();
                        foreach (var r in toRemove)
                            r?.Kill();
                        plugin.Puts($"Авто-очистка по типу '{kvp.Key}' (удалено {toRemove.Count} шт.) Порог: {plugin.config.AutoCleanTypeThreshold}");
                    }
                }
            }
        }

        public class BanSpamModule : ICleanerModule
        {
            private FCleaner plugin;
            private Dictionary<string, int> playerItemDropCounts = new Dictionary<string, int>();
            private Dictionary<string, Timer> resetTimers = new Dictionary<string, Timer>();
            public BanSpamModule(FCleaner plugin) { this.plugin = plugin; }
            public void Initialize()
            {
                plugin.permission.RegisterPermission(plugin.config.AdminPermission, plugin);
                plugin.permission.RegisterPermission(plugin.config.NotifyPermission, plugin);
            }
            public void OnServerInitialized() {}
            public void Unload()
            {
                foreach (var t in resetTimers.Values) t.Destroy();
                resetTimers.Clear();
                playerItemDropCounts.Clear();
            }
            public void OnEntitySpawned(BaseNetworkable entity) {}
            public void OnItemAction(Item item, string action, BasePlayer player)
            {
                if (action != "drop" || player == null) return;
                if (!plugin.config.BanEnabled) return;
                if (plugin.permission.UserHasPermission(player.UserIDString, plugin.config.AdminPermission)) return;
                string pid = player.UserIDString;
                if (!playerItemDropCounts.ContainsKey(pid))
                {
                    playerItemDropCounts[pid] = 1;
                    resetTimers[pid] = plugin.timer.Once(plugin.config.ItemDropResetTime, () => ResetDropCount(pid));
                }
                else
                {
                    playerItemDropCounts[pid]++;
                }
                int drops = playerItemDropCounts[pid];
                if (drops >= plugin.config.ItemDropWarningThreshold && drops < plugin.config.ItemDropBanThreshold)
                {
                    if (plugin.config.EnableDropWarnings)
                    {
                        string warnMsg = plugin.lang.GetMessage("DropWarning", plugin, player.UserIDString);
                        player.ChatMessage(string.Format(warnMsg, drops));
                    }
                    NotifyAdmins(player, drops);
                }
                else if (drops >= plugin.config.ItemDropBanThreshold)
                {
                    BanPlayer(player, plugin.config.BanReason);
                }
            }
            public void ForceCleanup() {}
            private void ResetDropCount(string pid)
            {
                playerItemDropCounts.Remove(pid);
                if (resetTimers.ContainsKey(pid))
                {
                    resetTimers[pid].Destroy();
                    resetTimers.Remove(pid);
                }
            }
            private void NotifyAdmins(BasePlayer p, int dropCount)
            {
                string msg = string.Format(plugin.lang.GetMessage("AdminNotify", plugin, p.UserIDString),
                    p.displayName, p.UserIDString);
                foreach (var bp in BasePlayer.activePlayerList)
                {
                    if (bp == null) continue;
                    if (plugin.permission.UserHasPermission(bp.UserIDString, plugin.config.NotifyPermission))
                        bp.ChatMessage(msg);
                }
                plugin.Puts(msg + $" (DropCount={dropCount})");
            }
            private void BanPlayer(BasePlayer bp, string reason)
            {
                string steamId = bp.UserIDString;
                var ip = bp.net?.connection?.ipaddress;
                if (!string.IsNullOrEmpty(ip))
                {
                    var split = ip.Split(':');
                    ip = split[0];
                }
                plugin.Puts(string.Format(plugin.lang.GetMessage("DropBan", plugin, steamId), bp.displayName, reason));
                if (plugin.config.UseExtendedBanSystem && plugin.config.ExtendedBanType.Equals("IP", StringComparison.OrdinalIgnoreCase))
                {
                    if (!string.IsNullOrEmpty(ip))
                    {
                        plugin.RunServerCommand($"{plugin.config.CustomBanCommand} {ip} {plugin.config.BanDuration} \"{reason}\"");
                        return;
                    }
                }
                if (!string.IsNullOrEmpty(plugin.config.CustomBanCommand))
                {
                    plugin.RunServerCommand($"{plugin.config.CustomBanCommand} {steamId} {plugin.config.BanDuration} \"{reason}\"");
                }
                else
                {
                    bp.IPlayer.Ban(reason);
                }
            }
        }

        public class MarkersModule : ICleanerModule
        {
            private FCleaner plugin;
            private List<BaseEntity> activeMarkers = new List<BaseEntity>();

            public MarkersModule(FCleaner plugin) { this.plugin = plugin; }
            public void Initialize() {}
            public void OnServerInitialized() {}
            public void Unload()
            {
                RemoveAllMarkers();
            }
            public void OnEntitySpawned(BaseNetworkable entity) {}
            public void OnItemAction(Item item, string action, BasePlayer player) {}
            public void ForceCleanup()
            {
                RemoveAllMarkers();
            }
            public void AddMarker(Vector3 position)
            {
                if (!plugin.config.ShowMarkersOnMap) return;

                var vendingMarker = GameManager.server.CreateEntity(
                    "assets/prefabs/deployable/vendingmachine/vending_mapmarker.prefab",
                    position) as VendingMachineMapMarker;
                if (vendingMarker != null)
                {
                    vendingMarker.markerShopName = plugin.config.MapMarkerName;
                    vendingMarker.Spawn();
                    activeMarkers.Add(vendingMarker);
                }
            }
            public void RemoveAllMarkers()
            {
                foreach (var m in activeMarkers)
                    m?.Kill();
                activeMarkers.Clear();
            }
        }

        public class CleanupModule : ICleanerModule
        {
            private FCleaner plugin;
            private MarkersModule markersModule;
            private Timer cleanTimer;
            private List<DroppedItem> pendingDroppedItems = new List<DroppedItem>();
            private List<PlayerCorpse> pendingPlayerCorpses = new List<PlayerCorpse>();
            private List<NPCPlayerCorpse> pendingNPCCorpses = new List<NPCPlayerCorpse>();
            private List<BaseNetworkable> pendingDropBackpacks = new List<BaseNetworkable>();
            private List<LootableCorpse> pendingLootableCorpses = new List<LootableCorpse>();
            private List<BaseNetworkable> pendingInactive = new List<BaseNetworkable>();
            private List<BaseNetworkable> pendingZeroPoint = new List<BaseNetworkable>();
            private List<Vector3> markerPositions = new List<Vector3>();

            public CleanupModule(FCleaner plugin, MarkersModule markersModule)
            {
                this.plugin = plugin;
                this.markersModule = markersModule;
            }
            public void Initialize() {}
            public void OnServerInitialized()
            {
                cleanTimer = plugin.timer.Repeat(plugin.config.CleanInterval, 0, PrepareForCleanup);
                if (plugin.config.EnableCleanupMessages)
                {
                    string msg = string.Format(plugin.lang.GetMessage("CleaningInterval", plugin, null),
                        plugin.config.CleanInterval);
                    plugin.BroadcastToAll($"<color={plugin.config.PrefixColor}>{plugin.config.Prefix}</color> {msg}");
                }
            }
            public void Unload()
            {
                cleanTimer?.Destroy();
            }
            public void OnEntitySpawned(BaseNetworkable entity) {}
            public void OnItemAction(Item item, string action, BasePlayer player) {}
            public void ForceCleanup()
            {
                plugin.Puts(plugin.lang.GetMessage("ForceCleanTriggered", plugin, null));
                if (plugin.config.EnableCleanupMessages)
                {
                    plugin.BroadcastToAll($"<color={plugin.config.PrefixColor}>{plugin.config.Prefix}</color> {plugin.lang.GetMessage("ForceCleanTriggered", plugin, null)}");
                }
                PreparePendingLists();
                CleanUpAll();
            }
            private void PrepareForCleanup()
            {
                PreparePendingLists();
                if (plugin.config.ShowMarkersOnMap)
                {
                    foreach (var pos in markerPositions)
                        markersModule.AddMarker(pos);
                }
                if (plugin.config.EnableCleanupMessages)
                {
                    string notification = string.Format(plugin.lang.GetMessage("CleaningNotification", plugin, null),
                        plugin.config.CleanNotificationTime);
                    plugin.BroadcastToAll($"<color={plugin.config.PrefixColor}>{plugin.config.Prefix}</color> {notification}");
                }
                plugin.timer.Once(plugin.config.CleanNotificationTime, CleanUpAll);
            }
            private void PreparePendingLists()
            {
                pendingDroppedItems.Clear();
                pendingPlayerCorpses.Clear();
                pendingNPCCorpses.Clear();
                pendingDropBackpacks.Clear();
                pendingLootableCorpses.Clear();
                pendingInactive.Clear();
                pendingZeroPoint.Clear();
                markerPositions.Clear();

                foreach (var ent in BaseNetworkable.serverEntities)
                {
                    if (ent == null || ent.IsDestroyed) continue;

                    if (plugin.IsEntityJetpack(ent)) continue;

                    bool hasTransform = ent.transform != null;

                    if (plugin.config.CleanInactiveEntities && (!ent.gameObject.activeSelf || ent.IsDestroyed))
                    {
                        pendingInactive.Add(ent);
                        if (hasTransform) markerPositions.Add(ent.transform.position);
                        continue;
                    }

                    if (plugin.config.CleanDroppedItems && ent is DroppedItem di)
                    {
                        var shortName = di.item?.info?.shortname;
                        if (!string.IsNullOrEmpty(shortName))
                        {
                            if (plugin.config.ExcludedItems.Contains(shortName)) continue;
                            if (plugin.config.AutoAddKeycardsToExcluded && shortName.Contains("keycard_"))
                            {
                                bool droppedByPlayer;
                                ulong uidVal = di.item.uid.Value;
                                if (!plugin.playerDroppedItems.TryGetValue(uidVal, out droppedByPlayer) || !droppedByPlayer)
                                    continue;
                            }
                            pendingDroppedItems.Add(di);
                            if (hasTransform) markerPositions.Add(ent.transform.position);
                        }
                        continue;
                    }

                    if (plugin.config.CleanPlayerCorpses && ent is PlayerCorpse pc)
                    {
                        pendingPlayerCorpses.Add(pc);
                        if (hasTransform) markerPositions.Add(ent.transform.position);
                        continue;
                    }

                    if (plugin.config.CleanNPCBackpacks && ent is NPCPlayerCorpse npcC)
                    {
                        pendingNPCCorpses.Add(npcC);
                        if (hasTransform) markerPositions.Add(ent.transform.position);
                        continue;
                    }

                    if (plugin.config.CleanDropBackpacks && ent.ShortPrefabName.Equals("item_drop_backpack", StringComparison.OrdinalIgnoreCase))
                    {
                        pendingDropBackpacks.Add(ent);
                        if (hasTransform) markerPositions.Add(ent.transform.position);
                        continue;
                    }

                    if (plugin.config.CleanLootableCorpses && ent is LootableCorpse lc)
                    {
                        if (lc.playerSteamID == 0)
                        {
                            pendingLootableCorpses.Add(lc);
                            if (hasTransform) markerPositions.Add(ent.transform.position);
                        }
                        continue;
                    }

                    if (hasTransform &&
                        Vector3.Distance(ent.transform.position, Vector3.zero) < 10f &&
                        plugin.config.CustomZeroPointItems.Any(x => ent.ShortPrefabName.Contains(x)))
                    {
                        pendingZeroPoint.Add(ent);
                        markerPositions.Add(ent.transform.position);
                    }
                }
            }
            private void CleanUpAll()
            {
                markersModule.RemoveAllMarkers();
                int cleanedDropped = pendingDroppedItems.Count;
                int cleanedPlayerCorpses = pendingPlayerCorpses.Count;
                int cleanedNPCBackpacks = pendingNPCCorpses.Count;
                int cleanedDropBackpacks = pendingDropBackpacks.Count;
                int cleanedLootableCorpses = pendingLootableCorpses.Count;
                int cleanedInactive = pendingInactive.Count;
                int cleanedZeroPoint = pendingZeroPoint.Count;
                int totalCleaned = cleanedDropped + cleanedPlayerCorpses + cleanedNPCBackpacks + cleanedDropBackpacks +
                                   cleanedLootableCorpses + cleanedInactive + cleanedZeroPoint;

                if (plugin.config.EnablePartialCleanup)
                {
                    CleanUpEntitiesInChunks(pendingDroppedItems, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingPlayerCorpses, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingNPCCorpses, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingDropBackpacks, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingLootableCorpses, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingInactive, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay);
                    CleanUpEntitiesInChunks(pendingZeroPoint, plugin.config.CleanupChunkSize, plugin.config.CleanupChunkDelay,
                        () => NotifyCleanupResult(totalCleaned, cleanedDropped, cleanedPlayerCorpses, cleanedNPCBackpacks, cleanedDropBackpacks, cleanedLootableCorpses, cleanedInactive, cleanedZeroPoint));
                }
                else
                {
                    KillAll(pendingDroppedItems);
                    KillAll(pendingPlayerCorpses);
                    KillAll(pendingNPCCorpses);
                    KillAll(pendingDropBackpacks);
                    KillAll(pendingLootableCorpses);
                    KillAll(pendingInactive);
                    KillAll(pendingZeroPoint);
                    NotifyCleanupResult(totalCleaned, cleanedDropped, cleanedPlayerCorpses, cleanedNPCBackpacks, cleanedDropBackpacks, cleanedLootableCorpses, cleanedInactive, cleanedZeroPoint);
                }
                RemoveDroppedItemRecords(pendingDroppedItems);
            }
            private void CleanUpEntitiesInChunks<T>(List<T> entities, int chunkSize, float delay, Action onComplete = null) where T : BaseNetworkable
            {
                if (entities == null || entities.Count == 0)
                {
                    onComplete?.Invoke();
                    return;
                }
                var localList = new List<T>(entities);
                void CleanChunk()
                {
                    if (localList.Count == 0)
                    {
                        onComplete?.Invoke();
                        return;
                    }
                    int count = Mathf.Min(chunkSize, localList.Count);
                    var chunk = localList.GetRange(0, count);
                    foreach (var e in chunk) e?.Kill();
                    localList.RemoveRange(0, count);
                    if (localList.Count > 0) plugin.timer.Once(delay, CleanChunk);
                    else onComplete?.Invoke();
                }
                CleanChunk();
            }
            private void KillAll<T>(List<T> list) where T : BaseNetworkable
            {
                foreach (var e in list) e?.Kill();
            }
            private void NotifyCleanupResult(int total, int dropped, int playerCorpses, int npcBackpacks,
                int dropBackpacks, int lootableCorpses, int inactive, int zeroPoint)
            {
                if (!plugin.config.EnableCleanupMessages) return;
                string msg = string.Format(
                    plugin.lang.GetMessage("TotalCleaned", plugin, null),
                    total, dropped, playerCorpses, npcBackpacks, dropBackpacks, lootableCorpses, inactive
                );
                plugin.BroadcastToAll($"<color={plugin.config.PrefixColor}>{plugin.config.Prefix}</color> {msg}");
            }
            private void RemoveDroppedItemRecords(List<DroppedItem> droppedItems)
            {
                foreach (var di in droppedItems)
                {
                    if (di?.item == null) continue;
                    ulong uidVal = di.item.uid.Value;
                    if (plugin.playerDroppedItems.ContainsKey(uidVal))
                        plugin.playerDroppedItems.Remove(uidVal);
                }
            }
        }

        private List<ICleanerModule> modules = new List<ICleanerModule>();
        private CollisionModule collisionModule;
        private AutoCleanByTypeModule autoCleanByTypeModule;
        private BanSpamModule banSpamModule;
        private MarkersModule markersModule;
        private CleanupModule cleanupModule;

        private void OnServerInitialized()
        {
            LoadConfig();
            collisionModule = new CollisionModule(this);
            autoCleanByTypeModule = new AutoCleanByTypeModule(this);
            banSpamModule = new BanSpamModule(this);
            markersModule = new MarkersModule(this);
            cleanupModule = new CleanupModule(this, markersModule);

            modules.Add(collisionModule);
            modules.Add(autoCleanByTypeModule);
            modules.Add(banSpamModule);
            modules.Add(markersModule);
            modules.Add(cleanupModule);

            foreach (var m in modules)
            {
                m.Initialize();
                m.OnServerInitialized();
            }
        }

        private void Unload()
        {
            foreach (var m in modules)
                m.Unload();
        }

        private void OnEntitySpawned(BaseNetworkable entity)
        {
            foreach (var m in modules)
                m.OnEntitySpawned(entity);
        }

        private void OnItemAction(Item item, string action, BasePlayer player)
        {
            foreach (var m in modules)
                m.OnItemAction(item, action, player);
            if (action == "drop" && item != null)
            {
                ulong itemUid = item.uid.Value;
                playerDroppedItems[itemUid] = true;
            }
        }

        [ConsoleCommand("fcleaner.forceclean")]
        private void ForceCleanCommand(ConsoleSystem.Arg arg)
        {
            foreach (var m in modules)
                m.ForceCleanup();
            arg?.ReplyWith(lang.GetMessage("ForceCleanTriggered", this, null));
        }
    }
}

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