liste os pontos chave deste plugin em imgles:
using System;
using System.Collections.Generic;
using System.Linq;
using Facepunch;
using Newtonsoft.Json;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Recyclerbox", "Marte6", "2.2.0")]
    [Description("Turn a large wooden or small wooden box into a Recycler.")]
    public class Recyclerbox : CovalencePlugin
    {
        #region Fields
        private Configuration config;
        private const string UiOverlayName = "RecyclerOverlay";
        private Item.Flag itemScrapedFlag = (Item.Flag)(1 << 78);
        private readonly Dictionary<ulong, (StorageContainer recyclerContainer, BasePlayer player)> activeRecyclers = new();
        private readonly Dictionary<ulong, (DroppedItemContainer droppedContainer, BasePlayer player)> droppedItemContainers = new();
        private const string LargeBoxPrefab = "assets/prefabs/deployable/large wood storage/box.wooden.large.prefab";
        private const string SmallBoxPrefab = "assets/prefabs/deployable/woodenbox/woodbox_deployed.prefab";
        private const string DroppedItemPrefab = "assets/prefabs/misc/item drop/item_drop.prefab";
        #endregion

        #region Permissions
        private const string PermissionGetRecycler = "recyclerbox.get";
        private const string PermissionTransformRecycler = "recyclerbox.transform";
        private const string PermissionUseVirtualRecycler = "recyclerbox.virtual";

        private readonly List<string> pluginPermissions = new List<string> { PermissionGetRecycler, PermissionTransformRecycler, PermissionUseVirtualRecycler };
        #endregion

        #region Initialization
        private void OnServerInitialized()
        {
            foreach (var permission in pluginPermissions)
                this.permission.RegisterPermission(permission, this);

            AddCovalenceCommand(config.GetRecyclerCommand, nameof(CommandGetRecyclerbox));
            AddCovalenceCommand(config.TransformRecyclerCommand, nameof(CommandTransformRecyclerbox));
            AddCovalenceCommand(config.UseVirtualRecyclerCommand, nameof(CommandUseVirtualRecycler));
        }

        private void Unload()
        {
            foreach (var player in BasePlayer.activePlayerList)
            {
                CuiHelper.DestroyUi(player, UiOverlayName);
                TerminateVirtualRecycler(player);
            }
            RemoveAllDroppedItemContainers();
        }
        #endregion

        #region NoEscape
        [PluginReference]
        private Plugin NoEscape;

        private bool IsPlayerRaidBlocked(BasePlayer player) => NoEscape?.Call<bool>("IsRaidBlocked", player) ?? false;
        #endregion

        #region Configuration
        protected class Configuration
        {
            [JsonProperty("Get Recyclerbox Command")]
            public string[] GetRecyclerCommand = { "getrec", "getrecycler", "recyclerbox" };

            [JsonProperty("Transform Recyclerbox Command")]
            public string[] TransformRecyclerCommand = { "trarec", "boxtorecycler", "transformrecycler", "transforbox" };

            [JsonProperty("Virtual Recyclerbox Command")]
            public string[] UseVirtualRecyclerCommand = { "rec", "vrec", "virtualrecycler" };

            [JsonProperty("Large Box SkinId Recycler")]
            public ulong LargeBoxSkinIdRecycler = 2994391115;

            [JsonProperty("Small Box SkinId Recycler")]
            public ulong SmallBoxSkinIdRecycler = 2988594458;

            [JsonProperty("Recycler Efficiency")]
            public float RecyclerEfficiency = 0.6f;

            [JsonProperty("Ratio Scrap")]
            public float ScrapRatio = 1.2f;

            [JsonProperty("Enable Prefab Fx")]
            public bool EnablePrefabEffects = true;

            [JsonProperty("Lock Recycler While In Use")]
            public bool LockRecyclerDuringUse = false;

            [JsonProperty("Disable During Raid Block (Requires No Escape plugin)")]
            public bool DisableDuringRaidBlock = true;

            [JsonProperty("Prefab Fx Open Recycler")]
            public string PrefabEffectOnOpen = "assets/bundled/prefabs/fx/build/promote_toptier.prefab";

            [JsonProperty("Prefab Fx Close Recycler")]
            public string PrefabEffectOnClose = "assets/bundled/prefabs/fx/build/promote_toptier.prefab";

            [JsonProperty("Transfer Virtual Recycled Items to Inventory")]
            public bool TransferRecycledItemsToInventory = false;

            [JsonProperty("Blacklisted Items")]
            public string[] BlacklistedItems = { "lmg.m249" };

            [JsonProperty("Blacklisted Skins")]
            public List<ulong> BlacklistedSkins = new() { 2994391115, 2988594458 };

            public string ToJson() => JsonConvert.SerializeObject(this);

            public Dictionary<string, object> ToDictionary() => JsonConvert.DeserializeObject<Dictionary<string, object>>(ToJson());
        }

        protected override void LoadDefaultConfig() => config = new Configuration();

        protected override void LoadConfig()
        {
            base.LoadConfig();
            try
            {
                config = Config.ReadObject<Configuration>() ?? throw new JsonException();
                var currentConfigDict = config.ToDictionary();
                var defaultConfigDict = new Configuration().ToDictionary();

                if (currentConfigDict.Count != defaultConfigDict.Count)
                {
                    Puts("Configuration appears to be outdated; updating and saving");
                    SaveConfig();
                }
                else
                {
                    foreach (var key in currentConfigDict.Keys)
                    {
                        if (!defaultConfigDict.ContainsKey(key))
                        {
                            Puts("Configuration appears to be outdated; updating and saving");
                            SaveConfig();
                            return;
                        }
                    }
                }
            }
            catch
            {
                Puts($"Configuration file {Name}.json is invalid; using defaults");
                LoadDefaultConfig();
            }
        }

        protected override void SaveConfig()
        {
            Puts($"Configuration changes saved to {Name}.json");
            Config.WriteObject(config, true);
        }
        #endregion Configuration

        #region Localization
        protected override void LoadDefaultMessages()
        {
            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["RecyclerTitle"] = "RECYCLER BOX",
                    ["RecyclerSubtitle"] = "Items are instantly recycled",
                    ["RecyclerItemMissingMsg"] = "Recyclerbox: You aren't holding a Large or Small Wooden Box.",
                    ["RecyclerCreatedMsg"] = "Recyclerbox: The Box has been converted into a Recycler.",
                    ["RecyclerAlreadyMsg"] = "Recyclerbox: This Box is currently a Recycler.",
                    ["NotAllowed"] = "You do not have permission to use that command.",
                    ["GotRecyclerbox"] = "Recyclerbox sent to inventory.",
                    ["Denied Privilege"] = "You cannot recycle within someone's building privilege",
                    ["Denied Swimming"] = "You cannot recycle while swimming",
                    ["Denied Falling"] = "You cannot recycle while falling",
                    ["Denied Mounted"] = "You cannot recycle while mounted",
                    ["Denied Wounded"] = "You cannot recycle while wounded",
                    ["Denied Ship"] = "You cannot recycle while on a ship",
                    ["Denied Elevator"] = "You cannot recycle while on an elevator",
                    ["Denied Balloon"] = "You cannot recycle while on a balloon",
                    ["Denied Safe Zone"] = "You cannot recycle while in a safe zone",
                    ["Denied Hook Denied"] = "You can't recycle right now",
                    ["RecycleDropped"] = "You left items in the recycler and dropped them on the ground!",
                },
                this
            );

            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["RecyclerTitle"] = "CAIXA RECICLADORA",
                    ["RecyclerSubtitle"] = "Itens são reciclados instantaneamente",
                    ["RecyclerItemMissingMsg"] = "Recyclerbox: Você não está segurando uma Caixa Grande ou Pequena de Madeira.",
                    ["RecyclerCreatedMsg"] = "Recyclerbox: Você transformou a Caixa em um Reciclador.",
                    ["RecyclerAlreadyMsg"] = "Recyclerbox: Esta Caixa já é um Reciclador.",
                    ["NotAllowed"] = "Você não tem permissão para usar esse comando.",
                    ["GotRecyclerbox"] = "Recyclerbox enviada para o inventário.",
                    ["Denied Privilege"] = "Você não pode reciclar dentro do privilégio de construção de outro player",
                    ["Denied Swimming"] = "Você não pode reciclar enquanto nada",
                    ["Denied Falling"] = "Você não pode reciclar enquanto cai",
                    ["Denied Mounted"] = "Você não pode reciclar enquanto estiver montado",
                    ["Denied Wounded"] = "Você não pode reciclar enquanto estiver ferido",
                    ["Denied Ship"] = "Você não pode reciclar enquanto estiver em um navio",
                    ["Denied Elevator"] = "Você não pode reciclar enquanto estiver em um elevador",
                    ["Denied Balloon"] = "Você não pode reciclar enquanto estiver em um balão",
                    ["Denied Safe Zone"] = "Você não pode reciclar enquanto estiver em uma zona segura",
                    ["Denied Hook Denied"] = "Você não pode reciclar agora",
                    ["RecycleDropped"] = "Você deixou itens no reciclador e os deixou cair no chão!",
                },
                this,
                "pt-BR"
            );

            lang.RegisterMessages(
                new Dictionary<string, string>
                {
                    ["RecyclerTitle"] = "RECYCLERBOX",
                    ["RecyclerSubtitle"] = "Gegenstände werden sofort recycelt",
                    ["RecyclerItemMissingMsg"] = "Recyclerbox: Du hast keine große oder kleine Holzkiste in der Hand.",
                    ["RecyclerCreatedMsg"] = "Recyclerbox: Die Kiste wurde in ein Recyclerbox umgewandelt.",
                    ["RecyclerAlreadyMsg"] = "Recyclerbox: Diese Kiste ist bereits eine Recyclerbox.",
                    ["NotAllowed"] = "Du hast keine Berechtigung, diesen Befehl zu benutzen.",
                    ["GotRecyclerbox"] = "Die Recyclerbox ist deinem Inventar gutgeschrieben worden.",
                    ["Denied Privilege"] = "Du kannst momentan nicht recyceln, du befindest dich in einem fremden Bauradius.",
                    ["Denied Swimming"] = "Du kannst momentan nicht recyceln, während Du schwimmst.",
                    ["Denied Falling"] = "Du kannst momentan nicht recyceln, während Du im freienfall bist.",
                    ["Denied Mounted"] = "Du kannst momentan nicht recyceln, während Du auf einem Fahrzeug sitzt.",
                    ["Denied Wounded"] = "Du kannst momentan nicht recyceln, während Du verletzt bist.",
                    ["Denied Ship"] = "Du kannst momentan nicht recyceln, da Du dich auf einem Schiff befindest.",
                    ["Denied Elevator"] = "Du kannst momentan nicht recyceln, da Du dich im Aufzug befindest.",
                    ["Denied Balloon"] = "Du kannst momentan nicht recyceln, da Du dich im Ballon befindest.",
                    ["Denied Safe Zone"] = "Du kannst momentan nicht recyceln, da Du dich in einer sicheren Zone befindest.",
                    ["Denied Hook Denied"] = "Du kannst momentan nicht recyceln.",
                    ["RecycleDropped"] = "Du hast Gegenstände im Recycler gelassen und auf den Boden fallen lassen!",
                },
                this,
                "de"
            );
        }
        #endregion Localization

        #region Hooks
        private void OnLootEntityEnd(BasePlayer player, StorageContainer container)
        {
            if ((container.skinID != config.LargeBoxSkinIdRecycler && container.skinID != config.SmallBoxSkinIdRecycler) || player == null)
                return;

            TerminateVirtualRecycler(player);
        }

        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            if (player == null)
                return;

            TerminateVirtualRecycler(player);

            if (droppedItemContainers.Count == 0)
                return;

            var temporaryContainerList = droppedItemContainers.ToList();

            foreach (var (uid, (container, target)) in temporaryContainerList)
            {
                if (!IsValid(container) || !IsValid(target) || target.userID == player.userID)
                {
                    if (IsValid(container))
                        container.Kill();

                    droppedItemContainers.Remove(uid);
                }
            }
        }

        private void CleanupDroppedContainers()
        {
            if (droppedItemContainers.Count == 0)
                return;

            var temporaryContainerList = new List<KeyValuePair<ulong, (DroppedItemContainer container, BasePlayer player)>>(droppedItemContainers);

            foreach (var (uid, (container, _)) in temporaryContainerList)
            {
                if (IsValid(container))
                    container.Kill();

                droppedItemContainers.Remove(uid);
            }
        }

        private void OnLootEntity(BasePlayer player, StorageContainer container)
        {
            if (container.skinID != config.LargeBoxSkinIdRecycler && container.skinID != config.SmallBoxSkinIdRecycler)
                return;

            container.inventory.capacity = 42;

            if (config.EnablePrefabEffects)
                Effect.server.Run(config.PrefabEffectOnOpen, player.GetNetworkPosition());

            if (config.LockRecyclerDuringUse)
                container.SetFlag(BaseEntity.Flags.Locked, true);

            CreateUi(player);
        }

        private void OnItemRemovedFromContainer(ItemContainer container, Item item)
        {
            if (IsValid(container?.entityOwner) && (container.entityOwner.skinID == config.LargeBoxSkinIdRecycler || container.entityOwner.skinID == config.SmallBoxSkinIdRecycler))
            {
                item.SetFlag(itemScrapedFlag, false);
            }
        }

        private void OnItemAddedToContainer(ItemContainer container, Item item)
        {
            if (IsValid(container?.entityOwner) && (container.entityOwner.skinID == config.LargeBoxSkinIdRecycler || container.entityOwner.skinID == config.SmallBoxSkinIdRecycler) && !item.flags.HasFlag(itemScrapedFlag))
            {
                var player = BasePlayer.FindByID(container.entityOwner.OwnerID);

                if (player != null && config.DisableDuringRaidBlock && IsPlayerRaidBlocked(player))
                    return;

                RecycleItem(container, item);
            }
        }

        private void OnPlayerLootEnd(PlayerLoot playerLoot)
        {
            if (playerLoot.baseEntity is not BasePlayer player)
                return;

            foreach (var container in playerLoot.containers)
            {
                if (IsValid(container?.entityOwner) && (container.entityOwner.skinID == config.LargeBoxSkinIdRecycler || container.entityOwner.skinID == config.SmallBoxSkinIdRecycler))
                {
                    CuiHelper.DestroyUi(player, UiOverlayName);
                    if (!container.entityOwner.IsBusy())
                    {
                        if (config.EnablePrefabEffects)
                            Effect.server.Run(config.PrefabEffectOnClose, player.GetNetworkPosition());

                        if (config.LockRecyclerDuringUse)
                            container.entityOwner.SetFlag(BaseEntity.Flags.Locked, false);
                    }
                }
            }
        }

        private object CanLootEntity(BasePlayer player, DroppedItemContainer container)
        {
            return container.IsValid() && droppedItemContainers.TryGetValue(container.net.ID.Value, out var data) && data.player.userID != player.userID ? true : null;
        }

        private void OnEntityKill(DroppedItemContainer container)
        {
            if (IsValid(container))
                droppedItemContainers.Remove(container.net.ID.Value);
        }

        private object CanAcceptItem(ItemContainer container, Item item, int targetPos)
        {
            if (IsValid(container?.entityOwner) && (container.entityOwner.skinID == config.LargeBoxSkinIdRecycler || container.entityOwner.skinID == config.SmallBoxSkinIdRecycler))
            {
                if (config.BlacklistedItems.Contains(item.info.shortname) || config.BlacklistedSkins.Contains(item.skin))
                {
                    return ItemContainer.CanAcceptResult.CannotAcceptRightNow;
                }
            }
            return null;
        }
        #endregion

        #region Functions
        void RecycleItem(ItemContainer container, Item item)
        {
            bool moveBackToPlayerInventory = false;
            BasePlayer player = null;

            if (config.TransferRecycledItemsToInventory && container.entityOwner.name == "Virtual")
            {
                moveBackToPlayerInventory = true;
                player = BasePlayer.FindByID(container.entityOwner.OwnerID);
                container = player.inventory.containerMain;
            }

            if (item.info.Blueprint == null)
            {
                AttemptToReturnItem(item, container, player, moveBackToPlayerInventory);
                return;
            }

            float efficiency = config.RecyclerEfficiency;

            if (item.hasCondition)
                efficiency *= Mathf.Clamp(item.conditionNormalized * item.maxConditionNormalized, 0.1f, 1f);

            int itemAmount = item.amount > 1 ? item.amount : 1;

            if (item.info.Blueprint.scrapFromRecycle > 0)
            {
                AttemptToProduceScrap(item, itemAmount, container, player, moveBackToPlayerInventory, efficiency);
            }

            item.UseItem(itemAmount);
            ProduceRecycleIngredients(item, itemAmount, container, moveBackToPlayerInventory, player, efficiency);
        }

        private void AttemptToReturnItem(Item item, ItemContainer container, BasePlayer player, bool returnItem)
        {
            if (returnItem)
            {
                if (!item.MoveToContainer(container))
                {
                    item.Drop(player.GetCenter(), player.GetDropVelocity());
                }
            }
        }

        private void AttemptToProduceScrap(Item item, int itemAmount, ItemContainer container, BasePlayer player, bool returnItem, float efficiency)
        {
            float scrapRatio = config.ScrapRatio;
            int scrapAmount = 0;

            try
            {
                float calculatedScrap = item.info.Blueprint.scrapFromRecycle * itemAmount * scrapRatio;
                scrapAmount = (int)calculatedScrap;

                if (calculatedScrap < 1f && UnityEngine.Random.Range(0f, 1f) >= scrapRatio)
                    return;

                scrapAmount = scrapAmount > 0 ? scrapAmount : 1;
                Item newItem = ItemManager.CreateByName("scrap", scrapAmount, 0);
                if (!returnItem)
                    newItem.SetFlag(itemScrapedFlag, true);

                if (!newItem.MoveToContainer(container) && player != null)
                {
                    newItem.Drop(player.GetCenter(), player.GetDropVelocity());
                }
            }
            catch
            {
                Puts($"Error recycling {item.info.shortname} for scrap {scrapAmount}", this);
            }
        }

        private void ProduceRecycleIngredients(Item item, int itemAmount, ItemContainer container, bool returnItem, BasePlayer player, float efficiency)
        {
            foreach (ItemAmount ingredient in item.info.Blueprint.ingredients)
            {
                if (ingredient.itemDef.shortname != "scrap")
                {
                    int ingredientsToProduce = Mathf.FloorToInt(itemAmount * efficiency * ingredient.amount / item.info.Blueprint.amountToCreate);

                    if (ingredientsToProduce < 1 && UnityEngine.Random.Range(0f, 1f) > 0.5f)
                        ingredientsToProduce = 1;

                    if (ingredientsToProduce > 0)
                    {
                        for (int i = 0; i < ingredientsToProduce; i += ingredient.itemDef.stackable)
                        {
                            int quantityToCreate = Mathf.Min(ingredientsToProduce - i, ingredient.itemDef.stackable);

                            try
                            {
                                Item newItem = ItemManager.Create(ingredient.itemDef, quantityToCreate, 0);
                                if (!returnItem)
                                    newItem.SetFlag(itemScrapedFlag, true);
                                if (!newItem.MoveToContainer(container) && player != null)
                                {
                                    newItem.Drop(player.GetCenter(), player.GetDropVelocity());
                                }
                            }
                            catch
                            {
                                Puts($"Error recycling {item.info.shortname} to ingredient {ingredient.itemDef.shortname}", this);
                            }
                        }
                    }
                }
            }
        }

        private void CreateUi(BasePlayer player)
        {
            var uiElements = new CuiElementContainer();

            var mainConfigPanel = uiElements.Add(
                new CuiPanel
                {
                    Image = { Color = "1 0.96 0.88 0.15" },
                    RectTransform =
                    {
                        AnchorMin = "0.5 0",
                        AnchorMax = "0.5 0",
                        OffsetMin = "192.5 610",
                        OffsetMax = "572.5 633",
                    },
                    CursorEnabled = false,
                },
                "Overlay",
                UiOverlayName
            );

            uiElements.Add(
                new CuiLabel
                {
                    Text =
                    {
                        Text = lang.GetMessage("RecyclerTitle", this, player.UserIDString),
                        Align = TextAnchor.MiddleCenter,
                        FontSize = 18,
                        Color = "1 1 1 1",
                        Font = "robotocondensed-bold.ttf",
                    },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 3" },
                },
                mainConfigPanel
            );

            uiElements.Add(
                new CuiLabel
                {
                    Text =
                    {
                        Text = lang.GetMessage("RecyclerSubtitle", this, player.UserIDString),
                        Align = TextAnchor.MiddleCenter,
                        FontSize = 14,
                        Color = "1 1 1 1",
                        Font = "robotocondensed-regular.ttf",
                    },
                    RectTransform = { AnchorMin = "0 0", AnchorMax = "1 1" },
                },
                mainConfigPanel
            );

            CuiHelper.AddUi(player, uiElements);
        }

        public bool IsValid(BaseNetworkable entity) => entity.IsValid() && !entity.IsDestroyed;

        private StorageContainer CreateVirtualRecycler(BasePlayer player)
        {
            var virtualRecyclerContainer = GameManager.server.CreateEntity(LargeBoxPrefab, player.transform.position.WithY(-5f)) as StorageContainer;
            virtualRecyclerContainer.skinID = config.LargeBoxSkinIdRecycler;

            if (virtualRecyclerContainer == null)
                return null;

            virtualRecyclerContainer.enableSaving = false;
            virtualRecyclerContainer.Spawn();

            if (!IsValid(virtualRecyclerContainer))
                return null;

            virtualRecyclerContainer.name = "Virtual";
            virtualRecyclerContainer.OwnerID = player.userID;
            virtualRecyclerContainer.SetFlag(BaseEntity.Flags.Locked, true);
            virtualRecyclerContainer.UpdateNetworkGroup();
            virtualRecyclerContainer.gameObject.layer = 0;
            virtualRecyclerContainer.SendNetworkUpdateImmediate(true);

            OpenPlayerContainer(player, virtualRecyclerContainer);
            activeRecyclers[virtualRecyclerContainer.net.ID.Value] = (virtualRecyclerContainer, player);

            return virtualRecyclerContainer;
        }

        private void OpenPlayerContainer(BasePlayer player, StorageContainer container)
        {
            player.Invoke(
                () =>
                {
                    if (!IsValid(container))
                        return;

                    player.EndLooting();
                    if (!player.inventory.loot.StartLootingEntity(container, false))
                        return;

                    player.inventory.loot.AddContainer(container.inventory);
                    player.inventory.loot.SendImmediate();
                    player.ClientRPC(RpcTarget.Player("RPC_OpenLootPanel", player), container.panelName);
                    player.SendNetworkUpdate();
                },
                0.2f
            );
        }

        private StorageContainer GetPlayerVirtualRecycler(ulong userId)
        {
            foreach (var (recycler, player) in activeRecyclers.Values)
            {
                if (player?.userID == userId)
                    return recycler;
            }
            return null;
        }

        private void EmptyRecyclerContents(StorageContainer recyclerBox, BasePlayer player)
        {
            if (player == null || player.inventory?.containerMain == null || recyclerBox?.inventory?.itemList.IsNullOrEmpty() != false)
                return;

            var itemList = Pool.Get<List<Item>>();
            itemList.AddRange(recyclerBox.inventory.itemList);

            if (itemList.Count == 0)
            {
                Pool.FreeUnmanaged(ref itemList);
                return;
            }

            player.ChatMessage(lang.GetMessage("RecycleDropped", this, player.UserIDString));

            var droppedContainer = GameManager.server.CreateEntity(DroppedItemPrefab, player.transform.position + Vector3.up) as DroppedItemContainer;
            if (droppedContainer == null)
                return;

            droppedContainer.enableSaving = false;
            droppedContainer.lootPanelName = "generic_resizable";
            droppedContainer.playerSteamID = player.userID;
            droppedContainer.TakeFrom(new[] { recyclerBox.inventory });
            droppedContainer.Spawn();

            if (IsValid(droppedContainer))
                droppedItemContainers[droppedContainer.net.ID.Value] = (droppedContainer, player);

            Pool.FreeUnmanaged(ref itemList);
        }

        private void TerminateVirtualRecycler(BasePlayer player)
        {
            StorageContainer virtualRecycler = GetPlayerVirtualRecycler(player.userID);
            if (IsValid(virtualRecycler) && activeRecyclers.Remove(virtualRecycler.net.ID.Value))
            {
                EmptyRecyclerContents(virtualRecycler, player);
                virtualRecycler.Kill();
            }
        }

        private void RemoveAllDroppedItemContainers()
        {
            if (droppedItemContainers.Count == 0)
                return;

            var temporaryContainerList = Pool.Get<List<KeyValuePair<ulong, (DroppedItemContainer container, BasePlayer player)>>>();
            temporaryContainerList.AddRange(droppedItemContainers);

            foreach (var (uid, (container, _)) in temporaryContainerList)
            {
                if (IsValid(container))
                    container.Kill();

                droppedItemContainers.Remove(uid);
            }

            Pool.FreeUnmanaged(ref temporaryContainerList);
        }

        private void OpenVirtualRecycler(BasePlayer player)
        {
            if (player == null || !CanPlayerAccessRecycler(player))
                return;

            TerminateVirtualRecycler(player);
            CreateVirtualRecycler(player);
        }

        private bool CanPlayerAccessRecycler(BasePlayer player)
        {
            if (player == null || !player.IsAlive())
                player.ChatMessage(lang.GetMessage("Denied Hook Denied", this, player.UserIDString));
            else if (player.IsWounded())
                player.ChatMessage(lang.GetMessage("Denied Wounded", this, player.UserIDString));
            else if (!player.CanBuild())
                player.ChatMessage(lang.GetMessage("Denied Privilege", this, player.UserIDString));
            else if (player.IsSwimming())
                player.ChatMessage(lang.GetMessage("Denied Swimming", this, player.UserIDString));
            else if (!player.IsOnGround() || player.IsFlying || player.isInAir)
                player.ChatMessage(lang.GetMessage("Denied Falling", this, player.UserIDString));
            else if (player.isMounted || player.GetParentEntity() is BaseMountable)
                player.ChatMessage(lang.GetMessage("Denied Mounted", this, player.UserIDString));
            else if (player.GetComponentInParent<CargoShip>())
                player.ChatMessage(lang.GetMessage("Denied Ship", this, player.UserIDString));
            else if (player.GetComponentInParent<HotAirBalloon>())
                player.ChatMessage(lang.GetMessage("Denied Balloon", this, player.UserIDString));
            else if (player.GetComponentInParent<Lift>())
                player.ChatMessage(lang.GetMessage("Denied Elevator", this, player.UserIDString));
            else
                return true;

            return false;
        }
        #endregion

        #region Commands
        private void CommandGetRecyclerbox(IPlayer iplayer, string command, string[] args)
        {
            if (!(iplayer.Object is BasePlayer player))
                return;

            if (!iplayer.HasPermission(PermissionGetRecycler))
            {
                player.ChatMessage(lang.GetMessage("NotAllowed", this, player.UserIDString));
                return;
            }

            Item recyclerBox = ItemManager.CreateByName("box.wooden.large");
            recyclerBox.skin = config.LargeBoxSkinIdRecycler;

            if (args.Length > 0 && args[0] == "small")
            {
                recyclerBox = ItemManager.CreateByName("box.wooden");
                recyclerBox.skin = config.SmallBoxSkinIdRecycler;
            }
            else
            {
                if (args.Length > 0)
                {
                    player.ChatMessage(lang.GetMessage("NotAllowed", this, player.UserIDString));
                    return;
                }
            }

            player.GiveItem(recyclerBox, BaseEntity.GiveItemReason.PickedUp);
            player.ChatMessage(lang.GetMessage("GotRecyclerbox", this, player.UserIDString));
        }

        private void CommandTransformRecyclerbox(IPlayer iplayer, string command, string[] args)
        {
            if (!(iplayer.Object is BasePlayer player))
                return;

            if (!iplayer.HasPermission(PermissionTransformRecycler))
            {
                player.ChatMessage(lang.GetMessage("NotAllowed", this, player.UserIDString));
                return;
            }

            Item activeItem = player.GetActiveItem();

            if (activeItem == null || (activeItem.info.shortname != "box.wooden.large" && activeItem.info.shortname != "box.wooden"))
            {
                player.ChatMessage(lang.GetMessage("RecyclerItemMissingMsg", this, player.UserIDString));
                return;
            }

            if (activeItem.skin == config.LargeBoxSkinIdRecycler || activeItem.skin == config.SmallBoxSkinIdRecycler)
            {
                player.ChatMessage(lang.GetMessage("RecyclerAlreadyMsg", this, player.UserIDString));
                return;
            }

            player.ChatMessage(lang.GetMessage("RecyclerCreatedMsg", this, player.UserIDString));
            activeItem.skin = activeItem.info.shortname == "box.wooden.large" ? config.LargeBoxSkinIdRecycler : config.SmallBoxSkinIdRecycler;
        }

        private void CommandUseVirtualRecycler(IPlayer iplayer, string command, string[] args)
        {
            if (!(iplayer.Object is BasePlayer player))
                return;

            if (!iplayer.HasPermission(PermissionUseVirtualRecycler))
            {
                player.ChatMessage(lang.GetMessage("NotAllowed", this, player.UserIDString));
                return;
            }

            OpenVirtualRecycler(player);
        }
        #endregion
    }
}