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

FREE Backpacks : 3.12.0 3.12.0

Нет прав для скачивания
C#:
// #define DEBUG_POOLING
// #define DEBUG_BACKPACK_LIFECYCLE

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Oxide.Core;
using Oxide.Core.Libraries.Covalence;
using Oxide.Core.Plugins;
using Oxide.Game.Rust.Cui;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using Network;
using Newtonsoft.Json.Converters;
using Oxide.Core.Configuration;
using Oxide.Core.Libraries;
using Rust;
using UnityEngine;
using UnityEngine.UI;
using Time = UnityEngine.Time;

namespace Oxide.Plugins
{
    [Info("Backpacks", "WhiteThunder", "3.12.0")]
    [Description("Allows players to have a Backpack which provides them extra inventory space.")]
    internal class Backpacks : CovalencePlugin
    {
        #region Fields

        private static int _maxCapacityPerPage = 48;

        private const int MinRows = 1;
        private const int MaxRows = 8;
        private const int MinCapacity = 1;
        private const int MaxCapacity = 48;
        private const int SlotsPerRow = 6;
        private const int ReclaimEntryMaxSize = 40;
        private const float StandardLootDelay = 0.1f;
        private const Item.Flag UnsearchableItemFlag = (Item.Flag)(1 << 24);

        private const string UsagePermission = "backpacks.use";
        private const string SizePermission = "backpacks.size";
        private const string GUIPermission = "backpacks.gui";
        private const string FetchPermission = "backpacks.fetch";
        private const string GatherPermission = "backpacks.gather";
        private const string RetrievePermission = "backpacks.retrieve";
        private const string AdminPermission = "backpacks.admin";
        private const string KeepOnDeathPermission = "backpacks.keepondeath";
        private const string LegacyKeepOnWipePermission = "backpacks.keeponwipe";
        private const string LegacyNoBlacklistPermission = "backpacks.noblacklist";

        private const string CoffinPrefab = "assets/prefabs/misc/halloween/coffin/coffinstorage.prefab";
        private const string DroppedBackpackPrefab = "assets/prefabs/misc/item drop/item_drop_backpack.prefab";
        private const string ResizableLootPanelName = "generic_resizable";

        private const int SaddleBagItemId = 1400460850;

        private readonly BackpackCapacityManager _backpackCapacityManager;
        private readonly BackpackManager _backpackManager;
        private readonly SubscriberManager _subscriberManager = new SubscriberManager();

        private ProtectionProperties _immortalProtection;
        private Effect _reusableEffect = new Effect();
        private string _cachedButtonUi;

        private readonly ApiInstance _api;
        private Configuration _config;
        private StoredData _storedData;
        private readonly HashSet<ulong> _uiViewers = new HashSet<ulong>();
        private Coroutine _saveRoutine;

        [PluginReference]
        private readonly Plugin Arena, BagOfHolding, BackpackButton, EventManager, ItemRetriever;

        public Backpacks()
        {
            _backpackCapacityManager = new BackpackCapacityManager(this);
            _backpackManager = new BackpackManager(this);
            _api = new ApiInstance(this);
        }

        #endregion

        #region Hooks

        private void Init()
        {
            permission.RegisterPermission(UsagePermission, this);
            permission.RegisterPermission(GUIPermission, this);
            permission.RegisterPermission(FetchPermission, this);
            permission.RegisterPermission(GatherPermission, this);
            permission.RegisterPermission(RetrievePermission, this);
            permission.RegisterPermission(AdminPermission, this);
            permission.RegisterPermission(KeepOnDeathPermission, this);

            _config.Init(this);

            _maxCapacityPerPage = Mathf.Clamp(_config.BackpackSize.MaxCapacityPerPage, MinCapacity, MaxCapacity);

            _backpackCapacityManager.Init(_config);

            PoolUtils.ResizePools();

            _storedData = StoredData.Load();

            Unsubscribe(nameof(OnPlayerSleep));
            Unsubscribe(nameof(OnPlayerSleepEnded));

            if (_config.GUI.Enabled)
            {
                AddCovalenceCommand("backpackgui", nameof(ToggleBackpackGUICommand));
            }
            else
            {
                Unsubscribe(nameof(OnPlayerConnected));
                Unsubscribe(nameof(OnNpcConversationStart));
                Unsubscribe(nameof(OnNpcConversationEnded));
            }
        }

        private void OnServerInitialized()
        {
            _immortalProtection = ScriptableObject.CreateInstance<ProtectionProperties>();
            _immortalProtection.name = "BackpacksProtection";
            _immortalProtection.Add(1);

            CheckBackpackButtonPlugin();
            RegisterAsItemSupplier();

            if (_config.GUI.Enabled)
            {
                Subscribe(nameof(OnPlayerSleep));
                Subscribe(nameof(OnPlayerSleepEnded));
                Subscribe(nameof(OnPlayerConnected));
                Subscribe(nameof(OnNpcConversationStart));
                Subscribe(nameof(OnNpcConversationEnded));

                foreach (var player in BasePlayer.activePlayerList)
                {
                    MaybeCreateButtonUi(player);
                }
            }
        }

        private void Unload()
        {
            UnityEngine.Object.Destroy(_immortalProtection);

            RestartSaveRoutine(async: false, keepInUseBackpacks: false);

            BackpackNetworkController.ResetNetworkGroupId();

            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyButtonUi(player);
            }

            PoolUtils.ResizePools(empty: true);
        }

        private void OnNewSave(string filename)
        {
            if (!_config.ClearOnWipe.Enabled)
                return;

            _backpackManager.ClearCache();

            IEnumerable<string> backpackFileNameList;
            try
            {
                backpackFileNameList = Interface.Oxide.DataFileSystem.GetFiles(Name)
                    .Select(fn => fn.Split(Path.DirectorySeparatorChar).Last()
                        .Replace(".json", string.Empty));
            }
            catch (DirectoryNotFoundException)
            {
                // No backpacks to clear.
                return;
            }

            var retainedDueToContents = 0;
            var retainedDueToPreferences = 0;
            var deletedBackpackFiles = 0;

            foreach (var backpackFileName in backpackFileNameList)
            {
                ulong userId;
                if (!ulong.TryParse(backpackFileName, out userId))
                    continue;

                var backpack = _backpackManager.GetBackpackIfExists(userId);
                if (backpack == null)
                    continue;

                backpack.EraseContents(_config.ClearOnWipe.GetForPlayer(backpackFileName));

                // Only delete the backpack data file if it's empty and has no saved preferences.
                if (backpack.HasItems || backpack.HasPreferences)
                {
                    backpack.SaveIfChanged();

                    if (backpack.HasItems)
                    {
                        retainedDueToContents++;
                    }
                    else if (backpack.HasPreferences)
                    {
                        retainedDueToPreferences++;
                    }
                }
                else
                {
                    _backpackManager.DeleteBackpackFile(userId);
                    deletedBackpackFiles++;
                }
            }

            _backpackManager.ClearCache();

            var logMessage = "New save created. Backpacks were wiped according to the config and player permissions.";
            if (deletedBackpackFiles > 0)
            {
                logMessage += $"\n- {deletedBackpackFiles} file(s) were deleted because those backpacks are now empty.";
            }

            if (retainedDueToContents > 0)
            {
                logMessage += $"\n- {retainedDueToContents} file(s) were retained because those backpacks were not empty after applying the player's wipe ruleset.";
            }

            if (retainedDueToPreferences > 0)
            {
                logMessage += $"\n- {retainedDueToPreferences} file(s) were retained even though those backpacks are empty because they contain player gather/retrieve preferences.";
            }

            LogWarning(logMessage);
        }

        private void OnServerSave()
        {
            RestartSaveRoutine(async: true, keepInUseBackpacks: true);
        }

        private void OnPluginLoaded(Plugin plugin)
        {
            switch (plugin.Name)
            {
                case nameof(BackpackButton):
                    CheckBackpackButtonPlugin();
                    break;
                case nameof(ItemRetriever):
                    RegisterAsItemSupplier();
                    break;
                case nameof(BagOfHolding):
                {
                    NextTick(() =>
                    {
                        if (!plugin.IsLoaded)
                            return;

                        _backpackManager.DiscoverBags(plugin);
                    });
                    break;
                }
            }
        }

        private void OnPluginUnloaded(Plugin plugin)
        {
            _subscriberManager.RemoveSubscriber(plugin);
        }

        private void OnPlayerDisconnected(BasePlayer player)
        {
            _backpackCapacityManager.ForgetCachedCapacity(player.userID);
            _backpackManager.GetBackpackIfCached(player.userID)?.NetworkController?.Unsubscribe(player);
        }

        // Handle player death by normal means.
        private void OnEntityDeath(BasePlayer player, HitInfo info) =>
            OnEntityKill(player);

        // Handle player death while sleeping in a safe zone.
        private void OnEntityKill(BasePlayer player)
        {
            if (player.IsNpc)
                return;

            DestroyButtonUi(player);

            if (!_backpackManager.HasBackpackFile(player.userID)
                || permission.UserHasPermission(player.UserIDString, KeepOnDeathPermission))
                return;

            if (_config.EraseOnDeath)
            {
                _backpackManager.TryEraseForPlayer(player.userID);
            }
            else if (_config.DropOnDeath)
            {
                _backpackManager.Drop(player.userID, player.transform.position);
            }
        }

        private void OnGroupPermissionGranted(string groupName, string perm)
        {
            if (!perm.StartsWith("backpacks"))
                return;

            if (perm.StartsWith(SizePermission) || perm.StartsWith(UsagePermission))
            {
                _backpackManager.HandleCapacityPermissionChangedForGroup(groupName);
            }
            else if (perm.StartsWith(RestrictionRuleset.FullPermissionPrefix) || perm.Equals(LegacyNoBlacklistPermission))
            {
                _backpackManager.HandleRestrictionPermissionChangedForGroup(groupName);
            }
            else if (perm.Equals(GatherPermission))
            {
                _backpackManager.HandleGatherPermissionChangedForGroup(groupName);
            }
            else if (perm.Equals(RetrievePermission))
            {
                _backpackManager.HandleRetrievePermissionChangedForGroup(groupName);
            }
            else if (_config.GUI.Enabled && perm.Equals(GUIPermission))
            {
                var groupName2 = groupName;
                foreach (var player in BasePlayer.activePlayerList.Where(p => permission.UserHasGroup(p.UserIDString, groupName2)))
                {
                    CreateOrDestroyButtonUi(player);
                }
            }
        }

        private void OnGroupPermissionRevoked(string groupName, string perm)
        {
            OnGroupPermissionGranted(groupName, perm);
        }

        private void OnUserPermissionGranted(string userId, string perm)
        {
            if (!perm.StartsWith("backpacks"))
                return;

            if (perm.StartsWith(SizePermission) || perm.StartsWith(UsagePermission))
            {
                _backpackManager.HandleCapacityPermissionChangedForUser(userId);
            }
            else if (perm.StartsWith(RestrictionRuleset.FullPermissionPrefix) || perm.Equals(LegacyNoBlacklistPermission))
            {
                _backpackManager.HandleRestrictionPermissionChangedForUser(userId);
            }
            else if (perm.Equals(GatherPermission))
            {
                _backpackManager.HandleGatherPermissionChangedForUser(userId);
            }
            else if (perm.Equals(RetrievePermission))
            {
                _backpackManager.HandleRetrievePermissionChangedForUser(userId);
            }
            else if (_config.GUI.Enabled && perm.Equals(GUIPermission))
            {
                var player = BasePlayer.Find(userId);
                if (player != null)
                {
                    CreateOrDestroyButtonUi(player);
                }
            }
        }

        private void OnUserPermissionRevoked(string userId, string perm)
        {
            OnUserPermissionGranted(userId, perm);
        }

        private void OnUserGroupAdded(string userId, string groupName)
        {
            _backpackManager.HandleGroupChangeForUser(userId);
        }

        private void OnUserGroupRemoved(string userId, string groupName)
        {
            _backpackManager.HandleGroupChangeForUser(userId);
        }

        // Only subscribed while the GUI button is enabled.
        private void OnPlayerConnected(BasePlayer player) => MaybeCreateButtonUi(player);

        private void OnPlayerRespawned(BasePlayer player)
        {
            MaybeCreateButtonUi(player);
            _backpackManager.GetBackpackIfCached(player.userID)?.PauseGatherMode(1f);
        }

        // Only subscribed while the GUI button is enabled.
        private void OnPlayerSleepEnded(BasePlayer player) => OnPlayerRespawned(player);

        // Only subscribed while the GUI button is enabled.
        private void OnPlayerSleep(BasePlayer player) => DestroyButtonUi(player);

        // Only subscribed while the GUI button is enabled.
        private void OnNpcConversationStart(NPCTalking npcTalking, BasePlayer player, ConversationData conversationData)
        {
            // This delay can be removed in the future if an OnNpcConversationStarted hook is created.
            NextTick(() =>
            {
                // Verify the conversation started, since another plugin may have blocked it.
                if (!npcTalking.conversingPlayers.Contains(player))
                    return;

                DestroyButtonUi(player);
            });
        }

        // Only subscribed while the GUI button is enabled.
        private void OnNpcConversationEnded(NPCTalking npcTalking, BasePlayer player) => MaybeCreateButtonUi(player);

        private void OnNetworkSubscriptionsUpdate(Network.Networkable networkable, List<Network.Visibility.Group> groupsToAdd, List<Network.Visibility.Group> groupsToRemove)
        {
            if (groupsToRemove == null)
                return;

            for (var i = groupsToRemove.Count - 1; i >= 0; i--)
            {
                var group = groupsToRemove[i];
                if (BackpackNetworkController.IsBackpackNetworkGroup(group))
                {
                    // Prevent automatically unsubscribing from backpack network groups.
                    // This allows the subscriptions to persist while players move around.
                    groupsToRemove.Remove(group);
                }
            }
        }

        #endregion

        #region API

        private class ApiInstance
        {
            public readonly Dictionary<string, object> ApiWrapper;

            private readonly Backpacks _plugin;
            private BackpackManager _backpackManager => _plugin._backpackManager;

            public ApiInstance(Backpacks plugin)
            {
                _plugin = plugin;

                ApiWrapper = new Dictionary<string, object>
                {
                    [nameof(AddSubscriber)] = new Action<Plugin, Dictionary<string, object>>(AddSubscriber),
                    [nameof(RemoveSubscriber)] = new Action<Plugin>(RemoveSubscriber),
                    [nameof(GetExistingBackpacks)] = new Func<Dictionary<ulong, ItemContainer>>(GetExistingBackpacks),
                    [nameof(EraseBackpack)] = new Action<ulong>(EraseBackpack),
                    [nameof(DropBackpack)] = new Func<BasePlayer, List<DroppedItemContainer>, DroppedItemContainer>(DropBackpack),
                    [nameof(GetBackpackOwnerId)] = new Func<ItemContainer, ulong>(GetBackpackOwnerId),
                    [nameof(IsBackpackLoaded)] = new Func<BasePlayer, bool>(IsBackpackLoaded),
                    [nameof(GetBackpackCapacity)] = new Func<BasePlayer, int>(GetBackpackCapacity),
                    [nameof(IsBackpackGathering)] = new Func<BasePlayer, bool>(IsBackpackGathering),
                    [nameof(IsBackpackRetrieving)] = new Func<BasePlayer, bool>(IsBackpackRetrieving),
                    [nameof(GetBackpackContainer)] = new Func<ulong, ItemContainer>(GetBackpackContainer),
                    [nameof(GetBackpackItemAmount)] = new Func<ulong, int, ulong, int>(GetBackpackItemAmount),
                    [nameof(TryOpenBackpack)] = new Func<BasePlayer, ulong, bool>(TryOpenBackpack),
                    [nameof(TryOpenBackpackContainer)] = new Func<BasePlayer, ulong, ItemContainer, bool>(TryOpenBackpackContainer),
                    [nameof(TryOpenBackpackPage)] = new Func<BasePlayer, ulong, int, bool>(TryOpenBackpackPage),
                    [nameof(SumBackpackItems)] = new Func<ulong, Dictionary<string, object>, int>(SumBackpackItems),
                    [nameof(CountBackpackItems)] = new Func<ulong, Dictionary<string, object>, int>(CountBackpackItems),
                    [nameof(TakeBackpackItems)] = new Func<ulong, Dictionary<string, object>, int, List<Item>, int>(TakeBackpackItems),
                    [nameof(TryDepositBackpackItem)] = new Func<ulong, Item, bool>(TryDepositBackpackItem),
                    [nameof(WriteBackpackContentsFromJson)] = new Action<ulong, string>(WriteBackpackContentsFromJson),
                    [nameof(ReadBackpackContentsAsJson)] = new Func<ulong, string>(ReadBackpackContentsAsJson),
                };
            }

            public void AddSubscriber(Plugin plugin, Dictionary<string, object> spec)
            {
                if (plugin == null)
                    throw new ArgumentNullException(nameof(plugin));

                if (spec == null)
                    throw new ArgumentNullException(nameof(spec));

                _plugin._subscriberManager.AddSubscriber(plugin, spec);
            }

            public void RemoveSubscriber(Plugin plugin)
            {
                if (plugin == null)
                    throw new ArgumentNullException(nameof(plugin));

                _plugin._subscriberManager.RemoveSubscriber(plugin);
            }

            public Dictionary<ulong, ItemContainer> GetExistingBackpacks()
            {
                return _backpackManager.GetAllCachedContainers();
            }

            public void EraseBackpack(ulong userId)
            {
                _backpackManager.TryEraseForPlayer(userId);
            }

            public DroppedItemContainer DropBackpack(BasePlayer player, List<DroppedItemContainer> collect)
            {
                var backpack = _backpackManager.GetBackpackIfExists(player.userID);
                if (backpack == null)
                    return null;

                return _backpackManager.Drop(player.userID, player.transform.position, collect);
            }

            public ulong GetBackpackOwnerId(ItemContainer container)
            {
                return _backpackManager.GetCachedBackpackForContainer(container)?.OwnerId ?? 0;
            }

            public bool IsBackpackLoaded(BasePlayer player)
            {
                return _backpackManager.GetBackpackIfCached(player.userID) != null;
            }

            public int GetBackpackCapacity(BasePlayer player)
            {
                return _plugin._backpackCapacityManager.GetCapacity(player.userID, player.UserIDString);
            }

            public bool IsBackpackGathering(BasePlayer player)
            {
                return _backpackManager.GetBackpackIfCached(player.userID)?.IsGathering ?? false;
            }

            public bool IsBackpackRetrieving(BasePlayer player)
            {
                return _backpackManager.GetBackpackIfCached(player.userID)?.IsRetrieving ?? false;
            }

            public ItemContainer GetBackpackContainer(ulong ownerId)
            {
                return _backpackManager.GetBackpackIfExists(ownerId)?.GetContainer(ensureContainer: true);
            }

            public int GetBackpackItemAmount(ulong ownerId, int itemId, ulong skinId)
            {
                var itemQuery = new ItemQuery { ItemId = itemId, SkinId = skinId };
                return _backpackManager.GetBackpackIfExists(ownerId)?.SumItems(ref itemQuery) ?? 0;
            }

            public bool TryOpenBackpack(BasePlayer player, ulong ownerId)
            {
                return _backpackManager.TryOpenBackpack(player, ownerId);
            }

            public bool TryOpenBackpackContainer(BasePlayer player, ulong ownerId, ItemContainer container)
            {
                return _backpackManager.TryOpenBackpackContainer(player, ownerId, container);
            }

            public bool TryOpenBackpackPage(BasePlayer player, ulong ownerId, int page)
            {
                return _backpackManager.TryOpenBackpackPage(player, ownerId, page);
            }

            public int SumBackpackItems(ulong ownerId, Dictionary<string, object> dict)
            {
                var itemQuery = ItemQuery.Parse(dict);
                return _backpackManager.GetBackpackIfExists(ownerId)?.SumItems(ref itemQuery) ?? 0;
            }

            public int CountBackpackItems(ulong ownerId, Dictionary<string, object> dict)
            {
                var backpack = _backpackManager.GetBackpackIfExists(ownerId);
                if (backpack == null)
                    return 0;

                if (dict == null)
                    return backpack.ItemCount;

                var itemQuery = ItemQuery.Parse(dict);
                return backpack.CountItems(ref itemQuery);
            }

            public int TakeBackpackItems(ulong ownerId, Dictionary<string, object> dict, int amount, List<Item> collect)
            {
                var itemQuery = ItemQuery.Parse(dict);
                return _backpackManager.GetBackpackIfExists(ownerId)?.TakeItems(ref itemQuery, amount, collect) ?? 0;
            }

            public bool TryDepositBackpackItem(ulong ownerId, Item item)
            {
                return _backpackManager.GetBackpack(ownerId).TryDepositItem(item);
            }

            public void WriteBackpackContentsFromJson(ulong ownerId, string json)
            {
                _backpackManager.GetBackpack(ownerId).WriteContentsFromJson(json);
            }

            public string ReadBackpackContentsAsJson(ulong ownerId)
            {
                return _backpackManager.GetBackpackIfExists(ownerId)?.SerializeContentsAsJson();
            }
        }

        [HookMethod(nameof(API_GetApi))]
        public Dictionary<string, object> API_GetApi()
        {
            return _api.ApiWrapper;
        }

        [HookMethod(nameof(API_AddSubscriber))]
        public void API_AddSubscriber(Plugin plugin, Dictionary<string, object> spec)
        {
            _api.AddSubscriber(plugin, spec);
        }

        [HookMethod(nameof(API_RemoveSubscriber))]
        public void API_RemoveSubscriber(Plugin plugin)
        {
            _api.RemoveSubscriber(plugin);
        }

        [HookMethod(nameof(API_GetExistingBackpacks))]
        public Dictionary<ulong, ItemContainer> API_GetExistingBackpacks()
        {
            return _api.GetExistingBackpacks();
        }

        [HookMethod(nameof(API_EraseBackpack))]
        public void API_EraseBackpack(ulong userId)
        {
            _api.EraseBackpack(userId);
        }

        [HookMethod(nameof(API_DropBackpack))]
        public DroppedItemContainer API_DropBackpack(BasePlayer player, List<DroppedItemContainer> collect = null)
        {
            return _api.DropBackpack(player, collect);
        }

        [HookMethod(nameof(API_GetBackpackOwnerId))]
        public object API_GetBackpackOwnerId(ItemContainer container)
        {
            return ObjectCache.Get(_api.GetBackpackOwnerId(container));
        }

        [HookMethod(nameof(API_IsBackpackLoaded))]
        public object API_IsBackpackLoaded(BasePlayer player)
        {
            return ObjectCache.Get(_api.IsBackpackLoaded(player));
        }

        [HookMethod(nameof(API_GetBackpackCapacity))]
        public object API_GetBackpackCapacity(BasePlayer player)
        {
            return ObjectCache.Get(_api.GetBackpackCapacity(player));
        }

        [HookMethod(nameof(API_IsBackpackGathering))]
        public object API_IsBackpackGathering(BasePlayer player)
        {
            return ObjectCache.Get(_api.IsBackpackGathering(player));
        }

        [HookMethod(nameof(API_IsBackpackRetrieving))]
        public object API_IsBackpackRetrieving(BasePlayer player)
        {
            return ObjectCache.Get(_api.IsBackpackRetrieving(player));
        }

        [HookMethod(nameof(API_GetBackpackContainer))]
        public ItemContainer API_GetBackpackContainer(ulong ownerId)
        {
            return _api.GetBackpackContainer(ownerId);
        }

        [HookMethod(nameof(API_GetBackpackItemAmount))]
        public int API_GetBackpackItemAmount(ulong ownerId, int itemId, ulong skinId = 0)
        {
            return _api.GetBackpackItemAmount(ownerId, itemId, skinId);
        }

        [HookMethod(nameof(API_TryOpenBackpack))]
        public object API_TryOpenBackpack(BasePlayer player, ulong ownerId = 0)
        {
            return ObjectCache.Get(_api.TryOpenBackpack(player, ownerId));
        }

        [HookMethod(nameof(API_TryOpenBackpackContainer))]
        public object API_TryOpenBackpackContainer(BasePlayer player, ulong ownerId, ItemContainer container)
        {
            return ObjectCache.Get(_api.TryOpenBackpackContainer(player, ownerId, container));
        }

        [HookMethod(nameof(API_TryOpenBackpackPage))]
        public object API_TryOpenBackpackPage(BasePlayer player, ulong ownerId = 0, int page = 0)
        {
            return ObjectCache.Get(_api.TryOpenBackpackPage(player, ownerId, page));
        }

        [HookMethod(nameof(API_SumBackpackItems))]
        public object API_SumBackpackItems(ulong ownerId, Dictionary<string, object> dict)
        {
            return ObjectCache.Get(_api.SumBackpackItems(ownerId, dict));
        }

        [HookMethod(nameof(API_CountBackpackItems))]
        public object API_CountBackpackItems(ulong ownerId, Dictionary<string, object> dict)
        {
            return ObjectCache.Get(_api.CountBackpackItems(ownerId, dict));
        }

        [HookMethod(nameof(API_TakeBackpackItems))]
        public object API_TakeBackpackItems(ulong ownerId, Dictionary<string, object> dict, int amount, List<Item> collect)
        {
            return ObjectCache.Get(_api.TakeBackpackItems(ownerId, dict, amount, collect));
        }

        [HookMethod(nameof(API_TryDepositBackpackItem))]
        public object API_TryDepositBackpackItem(ulong ownerId, Item item)
        {
            return ObjectCache.Get(_api.TryDepositBackpackItem(ownerId, item));
        }

        [HookMethod(nameof(API_WriteBackpackContentsFromJson))]
        public void API_WriteBackpackContentsFromJson(ulong ownerId, string json)
        {
            _api.WriteBackpackContentsFromJson(ownerId, json);
        }

        [HookMethod(nameof(API_ReadBackpackContentsAsJson))]
        public object API_ReadBackpackContentsAsJson(ulong ownerId)
        {
            return _api.ReadBackpackContentsAsJson(ownerId);
        }

        #endregion

        #region Exposed Hooks

        private static class ExposedHooks
        {
            public static object CanOpenBackpack(BasePlayer looter, ulong ownerId)
            {
                return Interface.CallHook("CanOpenBackpack", looter, ObjectCache.Get(ownerId));
            }

            public static void OnBackpackClosed(BasePlayer looter, ulong ownerId, ItemContainer container)
            {
                Interface.CallHook("OnBackpackClosed", looter, ObjectCache.Get(ownerId), container);
            }

            public static void OnBackpackOpened(BasePlayer looter, ulong ownerId, ItemContainer container)
            {
                Interface.CallHook("OnBackpackOpened", looter, ObjectCache.Get(ownerId), container);
            }

            public static object CanDropBackpack(ulong ownerId, Vector3 position)
            {
                return Interface.CallHook("CanDropBackpack", ObjectCache.Get(ownerId), position);
            }

            public static object CanEraseBackpack(ulong ownerId)
            {
                return Interface.CallHook("CanEraseBackpack", ObjectCache.Get(ownerId));
            }

            public static object CanBackpackAcceptItem(ulong ownerId, ItemContainer container, Item item)
            {
                return Interface.CallHook("CanBackpackAcceptItem", ObjectCache.Get(ownerId), container, item);
            }
        }

        #endregion

        #region Commands

        [Command("backpack", "backpack.open")]
        private void BackpackOpenCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, UsagePermission))
                return;

            OpenBackpack(
                basePlayer,
                IsKeyBindArg(args.LastOrDefault()),
                ParsePageArg(args.FirstOrDefault()),
                wrapAround: false
            );
        }

        [Command("backpack.next")]
        private void BackpackNextCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, UsagePermission))
                return;

            OpenBackpack(
                basePlayer,
                IsKeyBindArg(args.LastOrDefault())
            );
        }

        [Command("backpack.previous", "backpack.prev")]
        private void BackpackPreviousCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, UsagePermission))
                return;

            OpenBackpack(
                basePlayer,
                IsKeyBindArg(args.LastOrDefault()),
                forward: false
            );
        }

        [Command("backpack.fetch")]
        private void BackpackFetchCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, FetchPermission))
                return;

            if (args.Length < 2)
            {
                player.Reply(GetMessage(player, "Backpack Fetch Syntax"));
                return;
            }

            if (!VerifyCanOpenBackpack(basePlayer, basePlayer.userID))
                return;

            ItemDefinition itemDefinition;
            if (!VerifyValidItem(player, args[0], out itemDefinition))
                return;

            int desiredAmount;
            if (!int.TryParse(args[1], out desiredAmount) || desiredAmount < 1)
            {
                player.Reply(GetMessage(player, "Invalid Item Amount"));
                return;
            }

            var itemLocalizedName = itemDefinition.displayName.translated;
            var backpack = _backpackManager.GetBackpack(basePlayer.userID);

            var itemQuery = new ItemQuery { ItemDefinition = itemDefinition };

            var quantityInBackpack = backpack.SumItems(ref itemQuery);
            if (quantityInBackpack == 0)
            {
                player.Reply(string.Format(GetMessage(player, "Item Not In Backpack"), itemLocalizedName));
                return;
            }

            if (desiredAmount > quantityInBackpack)
            {
                desiredAmount = quantityInBackpack;
            }

            var amountTransferred = backpack.FetchItems(basePlayer, ref itemQuery, desiredAmount);
            if (amountTransferred <= 0)
            {
                player.Reply(string.Format(GetMessage(player, "Fetch Failed"), itemLocalizedName));
                return;
            }

            player.Reply(string.Format(GetMessage(player, "Items Fetched"), amountTransferred.ToString(), itemLocalizedName));
        }

        [Command("backpack.erase")]
        private void EraseBackpackCommand(IPlayer player, string cmd, string[] args)
        {
            if (!player.IsServer)
                return;

            ulong userId;
            if (args.Length < 1 || !ulong.TryParse(args[0], out userId))
            {
                player.Reply($"Syntax: {cmd} <id>");
                return;
            }

            if (!_backpackManager.TryEraseForPlayer(userId))
            {
                LogWarning($"Player {userId.ToString()} has no backpack to erase.");
                return;
            }

            LogWarning($"Erased backpack for player {userId.ToString()}.");
        }

        [Command("viewbackpack")]
        private void ViewBackpackCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, AdminPermission))
                return;

            if (args.Length < 1)
            {
                player.Reply(GetMessage(player, "View Backpack Syntax"));
                return;
            }

            string failureMessage;
            var targetPlayer = FindPlayer(player, args[0], out failureMessage);

            if (targetPlayer == null)
            {
                player.Reply(failureMessage);
                return;
            }

            var targetBasePlayer = targetPlayer.Object as BasePlayer;
            var desiredOwnerId = targetBasePlayer?.userID ?? ulong.Parse(targetPlayer.Id);

            OpenBackpack(
                basePlayer,
                IsKeyBindArg(args.LastOrDefault()),
                ParsePageArg(args.ElementAtOrDefault(1)),
                desiredOwnerId: desiredOwnerId
            );
        }

        // Alias for older versions of Player Administration (which should ideally not be calling this method directly).
        private void ViewBackpack(BasePlayer player, string cmd, string[] args) =>
            ViewBackpackCommand(player.IPlayer, cmd, args);

        private void ToggleBackpackGUICommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyPlayer(player, out basePlayer)
                || !VerifyHasPermission(player, GUIPermission))
                return;

            var enabledNow = _storedData.ToggleGuiButtonPreference(basePlayer.userID, _config.GUI.EnabledByDefault);
            if (enabledNow)
            {
                MaybeCreateButtonUi(basePlayer);
            }
            else
            {
                DestroyButtonUi(basePlayer);
            }

            player.Reply(GetMessage(player, "Toggled Backpack GUI"));
        }

        [Command("backpack.setgathermode")]
        private void SetGatherCommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyCanInteract(player, out basePlayer)
                || !VerifyHasPermission(player, UsagePermission)
                || !VerifyHasPermission(player, GatherPermission))
                return;

            var backpack = _backpackManager.GetBackpack(basePlayer.userID);
            if (!backpack.CanGather)
                return;

            GatherMode gatherMode;
            if (args.Length < 1 || !TryParseGatherMode(basePlayer, args[0], out gatherMode))
            {
                player.Reply(string.Format(GetMessage(basePlayer, "Set Gather Syntax"), cmd, GetGatherModeDisplayOptions(basePlayer)));
                return;
            }

            var oneBasedPageIndex = 1;
            if (args.Length >= 2 && !IsKeyBindArg(args[1]) && !int.TryParse(args[1], out oneBasedPageIndex))
            {
                player.Reply(string.Format(GetMessage(basePlayer, "Set Gather Syntax"), cmd, GetGatherModeDisplayOptions(basePlayer)));
                return;
            }

            if (oneBasedPageIndex < 1 || oneBasedPageIndex > backpack.PageCount)
            {
                player.Reply(string.Format(GetMessage(basePlayer, "Page Out Of Range"), backpack.PageCount));
                return;
            }

            var pageIndex = oneBasedPageIndex - 1;
            if (backpack.GetGatherModeForPage(pageIndex) == gatherMode)
                return;

            backpack.SetGatherModeForPage(basePlayer, pageIndex, gatherMode);
            player.Reply(string.Format(GetMessage(basePlayer, "Set Gather Mode Success"), oneBasedPageIndex, GetGatherModeDisplayString(basePlayer, gatherMode)));
        }

        [Command("backpack.ui.togglegather")]
        private void ToggleGatherUICommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyPlayer(player, out basePlayer))
                return;

            var lootingContainer = basePlayer.inventory.loot.containers.FirstOrDefault();
            if (lootingContainer == null)
                return;

            Backpack backpack;
            int pageIndex;
            if (!_backpackManager.IsBackpack(lootingContainer, out backpack, out pageIndex)
                || backpack.OwnerId != basePlayer.userID
                || !backpack.CanGather)
                return;

            backpack.ToggleGatherMode(basePlayer, pageIndex);
        }

        [Command("backpack.ui.toggleretrieve")]
        private void ToggleRetrieveUICommand(IPlayer player, string cmd, string[] args)
        {
            BasePlayer basePlayer;
            if (!VerifyPlayer(player, out basePlayer))
                return;

            var lootingContainer = basePlayer.inventory.loot.containers.FirstOrDefault();
            if (lootingContainer == null)
                return;

            Backpack backpack;
            int pageIndex;
            if (!_backpackManager.IsBackpack(lootingContainer, out backpack, out pageIndex)
                || pageIndex > 31
                || backpack.OwnerId != basePlayer.userID
                || !backpack.CanRetrieve)
                return;

            backpack.ToggleRetrieve(basePlayer, pageIndex);
        }

        #endregion

        #region Helper Methods

        public static void LogDebug(string message) => Interface.Oxide.LogDebug($"[Backpacks] {message}");
        public static void LogInfo(string message) => Interface.Oxide.LogInfo($"[Backpacks] {message}");
        public static void LogWarning(string message) => Interface.Oxide.LogWarning($"[Backpacks] {message}");
        public static void LogError(string message) => Interface.Oxide.LogError($"[Backpacks] {message}");

        private static T[] ParseEnumList<T>(string[] list, string errorFormat) where T : struct
        {
            var valueList = new List<T>(list?.Length ?? 0);

            if (list != null)
            {
                foreach (var itemName in list)
                {
                    T result;
                    if (Enum.TryParse(itemName, ignoreCase: true, result: out result))
                    {
                        valueList.Add(result);
                    }
                    else
                    {
                        LogError(string.Format(errorFormat, itemName));
                    }
                }
            }

            return valueList.ToArray();
        }

        private static bool IsKeyBindArg(string arg)
        {
            return arg == "True";
        }

        private static int ParsePageArg(string arg)
        {
            if (arg == null)
                return -1;

            int pageIndex;
            return int.TryParse(arg, out pageIndex)
                ? Math.Max(0, pageIndex - 1)
                : -1;
        }

        private static string DetermineLootPanelName(ItemContainer container)
        {
            return (container.entityOwner as StorageContainer)?.panelName
                   ?? (container.entityOwner as ContainerIOEntity)?.lootPanelName
                   ?? (container.entityOwner as LootableCorpse)?.lootPanelName
                   ?? (container.entityOwner as DroppedItemContainer)?.lootPanelName
                   ?? (container.entityOwner as BaseRidableAnimal)?.lootPanelName
                   ?? ResizableLootPanelName;
        }

        private static void ClosePlayerInventory(BasePlayer player)
        {
            player.ClientRPCPlayer(null, player, "OnRespawnInformation");
        }

        private static float CalculateOpenDelay(ItemContainer currentContainer, int nextContainerCapacity, bool isKeyBind = false)
        {
            if (currentContainer != null)
            {
                // Can instantly switch to a smaller container.
                if (nextContainerCapacity <= currentContainer.capacity)
                    return 0;

                // Can instantly switch to a generic resizable loot panel from a different loot panel.
                if (DetermineLootPanelName(currentContainer) != ResizableLootPanelName)
                    return 0;

                // Need a short delay so the generic_resizable loot panel can be redrawn properly.
                return StandardLootDelay;
            }

            // Can open instantly since not looting and chat is assumed to be closed.
            if (isKeyBind)
                return 0;

            // Not opening via key bind, so the chat window may be open.
            // Must delay in case the chat is still closing or else the loot panel may close instantly.
            return StandardLootDelay;
        }

        private static void StartLooting(BasePlayer player, ItemContainer container, StorageContainer entitySource)
        {
            if (player.CanInteract()
                && Interface.CallHook("CanLootEntity", player, entitySource) == null
                && player.inventory.loot.StartLootingEntity(entitySource, doPositionChecks: false))
            {
                player.inventory.loot.AddContainer(container);
                player.inventory.loot.SendImmediate();
                player.ClientRPCPlayer(null, player, "RPC_OpenLootPanel", entitySource.panelName);
            }
        }

        private static ItemContainer CreateItemContainer(int capacity, StorageContainer entityOwner)
        {
            var container = new ItemContainer();
            container.ServerInitialize(null, capacity);
            container.GiveUID();
            container.entityOwner = entityOwner;
            return container;
        }

        private static ItemContainer GetRootContainer(Item item)
        {
            var container = item.parent;
            if (container == null)
                return null;

            while (container.parent?.parent != null && container.parent != item)
            {
                container = container.parent.parent;
            }

            return container;
        }

        private bool TryParseGatherMode(BasePlayer player, string arg, out GatherMode gatherMode)
        {
            foreach (var enumValue in typeof(GatherMode).GetEnumValues())
            {
                var name = Enum.GetName(typeof(GatherMode), enumValue);
                var gatherModeValue = (GatherMode)enumValue;

                if (StringUtils.EqualsCaseInsensitive(arg, name))
                {
                    gatherMode = gatherModeValue;
                    return true;
                }

                var localizedName = GetMessage(player, GetGatherModeDisplayString(player, gatherModeValue));
                if (StringUtils.EqualsCaseInsensitive(arg, localizedName, StringComparison.InvariantCultureIgnoreCase))
                {
                    gatherMode = gatherModeValue;
                    return true;
                }
            }

            gatherMode = GatherMode.None;
            return false;
        }

        private string GetGatherModeDisplayOptions(BasePlayer player)
        {
            return string.Join("|", new List<string>
            {
                GetGatherModeDisplayString(player, GatherMode.All),
                GetGatherModeDisplayString(player, GatherMode.Existing),
                GetGatherModeDisplayString(player, GatherMode.None)
            });
        }

        private void SendEffect(BasePlayer player, string effectPrefab)
        {
            if (string.IsNullOrWhiteSpace(effectPrefab))
                return;

            _reusableEffect.Init(Effect.Type.Generic, player, 0, Vector3.zero, Vector3.forward);
            _reusableEffect.pooledString = effectPrefab;
            EffectNetwork.Send(_reusableEffect, player.net.connection);
        }

        private void CheckBackpackButtonPlugin()
        {
            if (BackpackButton == null
                || _config.UsingDefaults
                || !_config.GUI.Enabled)
                return;

            foreach (var player in BasePlayer.activePlayerList)
            {
                DestroyButtonUi(player);
            }

            _config.GUI.Enabled = false;
            SaveConfig();
            LogWarning($"Disabled GUI button in the config because the {nameof(BackpackButton)} plugin was detected.");
        }

        private void RegisterAsItemSupplier()
        {
            ItemRetriever?.Call("API_AddSupplier", this, new Dictionary<string, object>
            {
                ["FindPlayerItems"] = new Action<BasePlayer, Dictionary<string, object>, List<Item>>((player, rawItemQuery, collect) =>
                {
                    var backpack = _backpackManager.GetBackpackIfCached(player.userID);
                    if (backpack == null || !backpack.CanRetrieve || !backpack.CanAccess)
                        return;

                    var itemQuery = ItemQuery.Parse(rawItemQuery);
                    backpack.FindItems(ref itemQuery, collect, forItemRetriever: true);
                }),

                ["FindPlayerAmmo"] = new Action<BasePlayer, AmmoTypes, List<Item>>((player, ammoType, collect) =>
                {
                    var backpack = _backpackManager.GetBackpackIfCached(player.userID);
                    if (backpack == null || !backpack.CanRetrieve || !backpack.CanAccess)
                        return;

                    backpack.FindAmmo(ammoType, collect, forItemRetriever: true);
                }),

                ["SumPlayerIte
// ... [Code preview truncated due to length. Full source in attached file] ...

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