it comes after reloading plugin and first time entering ANY personal backpack from plugin. Only first time tho. So if 1 person triggers it it wont come for anybody in the console again. Unless I reload it, then it will come again on first trigger.
Merged post
I removed custom-items from backpack and it never triggered the spam. So it has with custom items to do. It hit me now I did my own version for this before your update, not perfect anywhere but it worked for me
// #define DEBUG_DROP_ON_DEATH
// #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 Facepunch;
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.15.5")]
[Description("Allows players to have a Backpack which provides them extra inventory space.")]
internal class Backpacks : CovalencePlugin
{
// Init gate to prevent early command use before capacity is ready
private static bool _initReady = false;
// FIXPOINT: Gate Backpacks until custom items stack is ready
[PluginReference] private Plugin CustomItemDefinitions;
[PluginReference] private Plugin CustomizableProtection;
[PluginReference] private Plugin CustomizableWeapons;
private bool DepsReady() => CustomItemDefinitions != null && CustomizableProtection != null && CustomizableWeapons != null;
// FIXPOINT: Ensure Init completes before ServerInitialized logic runs
private bool _lateInitDone = false;
private void EnsureReady(Action after)
{
if (!DepsReady() || !_lateInitDone)
{
timer.Once(1f, () => EnsureReady(after));
return;
}
after?.Invoke();
}
#region Fields
private static int _maxCapacityPerPage = 48;
private const int MinRows = 1;
private const int MaxRows = 8;
private const int MinContainerCapacity = 1;
private const int MaxContainerCapacity = 48;
private const int SlotsPerRow = 6;
private const float StandardLootDelay = 0.1f;
private const Item.Flag SearchableItemFlag = (Item.Flag)(1 << 24);
private const Item.Flag UnsearchableItemFlag = (Item.Flag)(1 << 25);
private const ItemDefinition.Flag SearchableItemDefinitionFlag = (ItemDefinition.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 AdminProtectedPermission = "backpacks.admin.protected";
private const string CapacityProfilePermission = "backpacks.size.profile";
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 CapacityManager _capacityManager;
private readonly BackpackManager _backpackManager;
private readonly SubscriberManager _subscriberManager = new();
private ProtectionProperties _immortalProtection;
private Effect _reusableEffect = new();
private string _cachedButtonUi;
private readonly ApiInstance _api;
private Configuration _config;
private PreferencesData _preferencesData;
private CapacityData _capacityData;
private readonly HashSet<ulong> _uiViewers = new();
private Coroutine _saveRoutine;
[PluginReference]
private readonly Plugin Arena, BackpackButton, EventManager, ItemRetriever;
public Backpacks()
{
_backpackManager = new BackpackManager(this);
_capacityManager = new CapacityManager(this, _backpackManager);
_api = new ApiInstance(this);
}
#endregion
#region Hooks
// FIXPOINT: Gate Init() until dependencies are present
private void Init()
{
if (!DepsReady())
{
timer.Once(1f, Init);
return;
}
LateInit();
}
private void LateInit()
{
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(AdminProtectedPermission, this);
permission.RegisterPermission(KeepOnDeathPermission, this);
_config.Init(this);
_maxCapacityPerPage = Mathf.Clamp(_config.BackpackSize.MaxCapacityPerPage, MinContainerCapacity, MaxContainerCapacity);
PoolUtils.ResizePools();
_preferencesData = PreferencesData.Load();
_capacityData = CapacityData.Exists() ? CapacityData.Load() : new CapacityData();
_capacityManager.Init(_config, _capacityData);
Unsubscribe(nameof(OnPlayerSleep));
Unsubscribe(nameof(OnPlayerSleepEnded));
if (_config.GUI.Enabled)
{
AddCovalenceCommand("backpackgui", nameof(ToggleBackpackGUICommand));
}
else
{
Unsubscribe(nameof(OnPlayerConnected));
Unsubscribe(nameof(OnNpcConversationStart));
Unsubscribe(nameof(OnNpcConversationEnded));
}
_lateInitDone = true; // FIXPOINT: signal that init is complete
}
// FIXPOINT: Run ServerInitialized after deps+init
private void OnServerInitialized()
{
EnsureReady(() =>
{
LateServerInit();
_initReady = true;
});
}
private void LateServerInit()
{
_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.BackpackSize.DynamicSize is { Enabled: true, CapacityResetOptions.Enabled: true })
{
_capacityData.Clear();
if (_capacityData.SaveIfChanged())
{
LogWarning("Dynamic size data has been reset.");
}
}
if (_config.ClearOnWipe.Enabled)
{
_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)
{
if (!ulong.TryParse(backpackFileName, out var 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;
}
}
private void OnPluginUnloaded(Plugin plugin)
{
_subscriberManager.RemoveSubscriber(plugin);
}
private void OnPlayerDisconnected(BasePlayer player)
{
_capacityManager.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.HasBackpack(player.userID))
return;
if (permission.UserHasPermission(player.UserIDString, KeepOnDeathPermission))
{
#if DEBUG_DROP_ON_DEATH
LogWarning($"[DEBUG_DROP_ON_DEATH] [Player {player.UserIDString}] Backpack not dropped because the player has the {KeepOnDeathPermission} permission.");
#endif
return;
}
if (_config.EraseOnDeath)
{
_backpackManager.TryEraseForPlayer(player.userID);
}
else if (_config.DropOnDeath)
{
_backpackManager.Drop(player.userID, player.transform.position);
}
else
{
#if DEBUG_DROP_ON_DEATH
LogWarning($"[DEBUG_DROP_ON_DEATH] [Player {player.UserIDString}] Backpack not dropped because \"Drop on Death (true/false)\" is set to false in the config.");
#endif
}
}
private void OnGroupPermissionGranted(string groupName, string perm)
{
if (!perm.StartsWith("backpacks"))
return;
if (perm.StartsWith(SizePermission) || perm.StartsWith(UsagePermission) || perm.StartsWith(CapacityProfilePermission))
{
_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) || perm.StartsWith(CapacityProfilePermission))
{
_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)
{
// People reported NRE on Carbon framework, meaning the player is somehow null, unknown root cause.
if (player == null)
return;
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(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(IsDynamicCapacityEnabled)] = new Func<bool>(IsDynamicCapacityEnabled),
[nameof(GetBackpackCapacity)] = new Func<BasePlayer, int>(GetBackpackCapacity),
[nameof(GetBackpackCapacityById)] = new Func<ulong, string, int>(GetBackpackCapacityById),
[nameof(GetBackpackInitialCapacity)] = new Func<BasePlayer, int>(GetBackpackInitialCapacity),
[nameof(GetBackpackMaxCapacity)] = new Func<BasePlayer, int>(GetBackpackMaxCapacity),
[nameof(AddBackpackCapacity)] = new Func<BasePlayer, int, int>(AddBackpackCapacity),
[nameof(SetBackpackCapacity)] = new Func<BasePlayer, int, int>(SetBackpackCapacity),
[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(MutateBackpackItems)] = new Func<ulong, Dictionary<string, object>, Dictionary<string, object>, int>(MutateBackpackItems),
[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 bool IsDynamicCapacityEnabled()
{
return _plugin._config.BackpackSize.DynamicSize.Enabled;
}
public int GetBackpackCapacity(BasePlayer player)
{
return _plugin._capacityManager.GetCapacity(player.userID, player.UserIDString);
}
public int GetBackpackCapacityById(ulong playerID, string playerIDString)
{
return _plugin._capacityManager.GetCapacity(playerID, playerIDString);
}
public int GetBackpackInitialCapacity(BasePlayer player)
{
return _plugin._capacityManager.GetInitialCapacity(player.userID, player.UserIDString);
}
public int GetBackpackMaxCapacity(BasePlayer player)
{
return _plugin._capacityManager.GetMaxCapacity(player.userID, player.UserIDString);
}
public int AddBackpackCapacity(BasePlayer player, int amount)
{
return _plugin._capacityManager.AddCapacity(player, amount);
}
public int SetBackpackCapacity(BasePlayer player, int capacity)
{
return _plugin._capacityManager.SetCapacity(player, capacity);
}
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 int MutateBackpackItems(ulong ownerId, Dictionary<string, object> itemQueryDict, Dictionary<string, object> mutationRequestDict)
{
var itemQuery = ItemQuery.Parse(itemQueryDict);
var mutationRequest = MutationRequest.Parse(mutationRequestDict);
return _backpackManager.GetBackpackIfExists(ownerId)?.MutateItems(ref itemQuery, ref mutationRequest) ?? 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);
}
// Deprecated, only returns container for first page. Use higher level APIs instead.
[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_EraseBackpack))]
public void API_EraseBackpack(EncryptedValue<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_IsDynamicCapacityEnabled))]
public object API_IsDynamicCapacityEnabled(BasePlayer player)
{
return ObjectCache.Get(_api.IsDynamicCapacityEnabled());
}
[HookMethod(nameof(API_GetBackpackCapacity))]
public object API_GetBackpackCapacity(BasePlayer player)
{
return ObjectCache.Get(_api.GetBackpackCapacity(player));
}
[HookMethod(nameof(API_GetBackpackCapacityById))]
public object API_GetBackpackCapacityById(ulong playerID, string playerIDString)
{
return ObjectCache.Get(_api.GetBackpackCapacityById(playerID, playerIDString));
}
[HookMethod(nameof(API_GetBackpackCapacityById))]
public object API_GetBackpackCapacityById(EncryptedValue<ulong> playerID, string playerIDString)
{
return ObjectCache.Get(_api.GetBackpackCapacityById(playerID, playerIDString));
}
[HookMethod(nameof(API_GetBackpackInitialCapacity))]
public object API_GetBackpackInitialCapacity(BasePlayer player)
{
return ObjectCache.Get(_api.GetBackpackInitialCapacity(player));
}
[HookMethod(nameof(API_GetBackpackMaxCapacity))]
public object API_GetBackpackMaxCapacity(BasePlayer player)
{
return ObjectCache.Get(_api.GetBackpackMaxCapacity(player));
}
[HookMethod(nameof(API_AddBackpackCapacity))]
public object API_AddBackpackCapacity(BasePlayer player, int amount)
{
return ObjectCache.Get(_api.AddBackpackCapacity(player, amount));
}
[HookMethod(nameof(API_SetBackpackCapacity))]
public object API_SetBackpackCapacity(BasePlayer player, int capacity)
{
return ObjectCache.Get(_api.SetBackpackCapacity(player, capacity));
}
[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));
}
// Deprecated, only returns container for first page. Use higher level APIs instead.
[HookMethod(nameof(API_GetBackpackContainer))]
public ItemContainer API_GetBackpackContainer(ulong ownerId)
{
return _api.GetBackpackContainer(ownerId);
}
[HookMethod(nameof(API_GetBackpackContainer))]
public ItemContainer API_GetBackpackContainer(EncryptedValue<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_GetBackpackItemAmount))]
public int API_GetBackpackItemAmount(EncryptedValue<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_TryOpenBackpack))]
public object API_TryOpenBackpack(BasePlayer player, EncryptedValue<ulong> ownerId = default)
{
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_TryOpenBackpackContainer))]
public object API_TryOpenBackpackContainer(BasePlayer player, EncryptedValue<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_TryOpenBackpackPage))]
public object API_TryOpenBackpackPage(BasePlayer player, EncryptedValue<ulong> ownerId = default, 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_SumBackpackItems))]
public object API_SumBackpackItems(EncryptedValue<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_CountBackpackItems))]
public object API_CountBackpackItems(EncryptedValue<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_TakeBackpackItems))]
public object API_TakeBackpackItems(EncryptedValue<ulong> ownerId, Dictionary<string, object> dict, int amount, List<Item> collect)
{
return ObjectCache.Get(_api.TakeBackpackItems(ownerId, dict, amount, collect));
}
[HookMethod(nameof(API_MutateBackpackItems))]
public object API_MutateBackpackItems(ulong ownerId, Dictionary<string, object> itemQueryDict, Dictionary<string, object> mutationRequestDict)
{
return ObjectCache.Get(_api.MutateBackpackItems(ownerId, itemQueryDict, mutationRequestDict));
}
[HookMethod(nameof(API_MutateBackpackItems))]
public object API_MutateBackpackItems(EncryptedValue<ulong> ownerId, Dictionary<string, object> itemQueryDict, Dictionary<string, object> mutationRequestDict)
{
return ObjectCache.Get(_api.MutateBackpackItems(ownerId, itemQueryDict, mutationRequestDict));
}
[HookMethod(nameof(API_TryDepositBackpackItem))]
public object API_TryDepositBackpackItem(ulong ownerId, Item item)
{
return ObjectCache.Get(_api.TryDepositBackpackItem(ownerId, item));
}
[HookMethod(nameof(API_TryDepositBackpackItem))]
public object API_TryDepositBackpackItem(EncryptedValue<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_WriteBackpackContentsFromJson))]
public void API_WriteBackpackContentsFromJson(EncryptedValue<ulong> ownerId, string json)
{
_api.WriteBackpackContentsFromJson(ownerId, json);
}
[HookMethod(nameof(API_ReadBackpackContentsAsJson))]
public object API_ReadBackpackContentsAsJson(ulong ownerId)
{
return _api.ReadBackpackContentsAsJson(ownerId);
}
[HookMethod(nameof(API_ReadBackpackContentsAsJson))]
public object API_ReadBackpackContentsAsJson(EncryptedValue<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 void OnBackpackDropped(ulong ownerId, List<DroppedItemContainer> droppedBackpackList)
{
Interface.CallHook("OnBackpackDropped", ownerId, droppedBackpackList);
}
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)
{
if (!_initReady)
{
player.Reply("Backpacks is still initializing. Try again in a moment.");
return;
}
if (!VerifyCanInteract(player, out var 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)
{
if (!VerifyCanInteract(player, out var basePlayer)
|| !VerifyHasPermission(player, UsagePermission))
return;
OpenBackpack(
basePlayer,
IsKeyBindArg(args.LastOrDefault())
);
}
[Command("backpack.previous", "backpack.prev")]
private void BackpackPreviousCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyCanInteract(player, out var basePlayer)
|| !VerifyHasPermission(player, UsagePermission))
return;
OpenBackpack(
basePlayer,
IsKeyBindArg(args.LastOrDefault()),
forward: false
);
}
[Command("backpack.fetch")]
private void BackpackFetchCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyCanInteract(player, out var basePlayer)
|| !VerifyHasPermission(player, FetchPermission))
return;
if (args.Length < 2)
{
ReplyToPlayer(player, LangEntry.BackpackFetchSyntax);
return;
}
if (!VerifyCanOpenBackpack(basePlayer, basePlayer.userID))
return;
if (!VerifyValidItem(player, args[0], out var itemDefinition))
return;
if (!int.TryParse(args[1], out var desiredAmount) || desiredAmount < 1)
{
ReplyToPlayer(player, LangEntry.InvalidItemAmount);
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)
{
ReplyToPlayer(player, LangEntry.ItemNotInBackpack, itemLocalizedName);
return;
}
if (desiredAmount > quantityInBackpack)
{
desiredAmount = quantityInBackpack;
}
var amountTransferred = backpack.FetchItems(basePlayer, ref itemQuery, desiredAmount);
if (amountTransferred <= 0)
{
ReplyToPlayer(player, LangEntry.FetchFailed, itemLocalizedName);
return;
}
ReplyToPlayer(player, LangEntry.ItemsFetched, amountTransferred, itemLocalizedName);
}
[Command("backpack.erase")]
private void EraseBackpackCommand(IPlayer player, string cmd, string[] args)
{
if (!player.IsServer)
return;
if (args.Length < 1 || !ulong.TryParse(args[0], out var 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)
{
if (!VerifyCanInteract(player, out var basePlayer)
|| !VerifyHasPermission(player, AdminPermission))
return;
if (args.Length < 1)
{
ReplyToPlayer(player, LangEntry.ViewBackpackSyntax);
return;
}
if (!VerifyTargetPlayer(player, args[0], out var targetPlayerId, out var targetPlayerIdString))
return;
if (permission.UserHasPermission(targetPlayerIdString, AdminProtectedPermission))
{
ReplyToPlayer(player, LangEntry.ViewBackpackProtected);
return;
}
OpenBackpack(
basePlayer,
IsKeyBindArg(args.LastOrDefault()),
ParsePageArg(args.ElementAtOrDefault(1)),
desiredOwnerId: targetPlayerId
);
}
// 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);
[Command("backpack.addsize")]
private void AddBackpackCapacityCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyHasPermission(player, AdminPermission))
return;
if (args.Length < 2 || !int.TryParse(args[1], out var amount))
{
ReplyToPlayer(player, LangEntry.BackpackCapacitySyntax, cmd);
return;
}
if (!VerifyTargetPlayer(player, args[0], out var targetPlayerId, out var targetPlayerIdString)
|| !VerifyDynamicCapacityEnabled(player))
return;
var newCapacity = _capacityManager.AddCapacity(targetPlayerId, targetPlayerIdString, amount);
ReplyToPlayer(player, LangEntry.ChangeCapacitySuccess, targetPlayerId, newCapacity);
}
[Command("backpack.setsize")]
private void SetBackpackCapacityCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyHasPermission(player, AdminPermission))
return;
if (args.Length < 2 || !int.TryParse(args[1], out var amount))
{
ReplyToPlayer(player, LangEntry.BackpackCapacitySyntax, cmd);
return;
}
if (!VerifyTargetPlayer(player, args[0], out var targetPlayerId, out var targetPlayerIdString)
|| !VerifyDynamicCapacityEnabled(player))
return;
var newCapacity = _capacityManager.SetCapacity(targetPlayerId, targetPlayerIdString, amount);
ReplyToPlayer(player, LangEntry.ChangeCapacitySuccess, targetPlayerId, newCapacity);
}
private void ToggleBackpackGUICommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyPlayer(player, out var basePlayer)
|| !VerifyHasPermission(player, GUIPermission))
return;
var enabledNow = _preferencesData.ToggleGuiButtonPreference(basePlayer.userID, _config.GUI.EnabledByDefault);
if (enabledNow)
{
MaybeCreateButtonUi(basePlayer);
}
else
{
DestroyButtonUi(basePlayer);
}
ReplyToPlayer(player, LangEntry.ToggledBackpackGUI);
}
[Command("backpack.setgathermode")]
private void SetGatherCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyCanInteract(player, out var basePlayer)
|| !VerifyHasPermission(player, UsagePermission)
|| !VerifyHasPermission(player, GatherPermission))
return;
var backpack = _backpackManager.GetBackpack(basePlayer.userID);
if (!backpack.CanGather)
return;
if (args.Length < 1 || !TryParseGatherMode(basePlayer, args[0], out var gatherMode))
{
ReplyToPlayer(player, LangEntry.SetGatherSyntax, cmd, GetGatherModeDisplayOptions(basePlayer));
return;
}
var oneBasedPageIndex = 1;
if (args.Length >= 2 && !IsKeyBindArg(args[1]) && !int.TryParse(args[1], out oneBasedPageIndex))
{
ReplyToPlayer(player, LangEntry.SetGatherSyntax, cmd, GetGatherModeDisplayOptions(basePlayer));
return;
}
if (oneBasedPageIndex < 1 || oneBasedPageIndex > backpack.PageCount)
{
ReplyToPlayer(player, LangEntry.PageOutOfRange, backpack.PageCount);
return;
}
var pageIndex = oneBasedPageIndex - 1;
if (backpack.GetGatherModeForPage(pageIndex) == gatherMode)
return;
backpack.SetGatherModeForPage(basePlayer, pageIndex, gatherMode);
ReplyToPlayer(player, LangEntry.SetGatherModeSuccess, oneBasedPageIndex, GetGatherModeDisplayString(basePlayer, gatherMode));
}
[Command("backpack.ui.togglegather")]
private void ToggleGatherUICommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyPlayer(player, out var basePlayer))
return;
var lootingContainer = basePlayer.inventory.loot.containers.FirstOrDefault();
if (lootingContainer == null)
return;
if (!_backpackManager.IsBackpack(lootingContainer, out var backpack, out var 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)
{
if (!VerifyPlayer(player, out var basePlayer))
return;
var lootingContainer = basePlayer.inventory.loot.containers.FirstOrDefault();
if (lootingContainer == null)
return;
if (!_backpackManager.IsBackpack(lootingContainer, out var backpack, out var pageIndex)
|| pageIndex > 31
|| backpack.OwnerId != basePlayer.userID
|| !backpack.CanRetrieve)
return;
backpack.ToggleRetrieve(basePlayer, pageIndex);
}
[Command("backpack.debug.gather")]
private void DebugGatherCommand(IPlayer player, string cmd, string[] args)
{
if (!VerifyHasPermission(player, AdminPermission))
return;
if (args.Length < 1)
{
player.Reply($"Syntax: {cmd} [player]");
return;
}
if (!VerifyTargetPlayer(player, args[0], out var targetPlayerId, out var targetPlayerIdString))
return;
var targetBackpack = _backpackManager.GetBackpackIfCached(targetPlayerId);
if (targetBackpack == null)
{
player.Reply($"Player {targetPlayerIdString} backpack is not initialized.");
return;
}
var sb = new StringBuilder();
sb.Append("Player: ").AppendLine(targetPlayerIdString);
sb.Append("NumPagesGatherEnabled: ").Append(targetBackpack.NumPagesGathering).AppendLine();
sb.Append("IsGathering: ").Append(targetBackpack.IsGathering).AppendLine();
sb.Append("CanGather: ").Append(targetBackpack.CanGather).AppendLine();
var targetPlayer = BasePlayer.FindByID(targetPlayerId);
if (targetPlayer != null)
{
var watcher = targetPlayer.GetComponent<InventoryWatcher>();
if (watcher != null)
{
sb.Append("DetailedStats: ").AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherSucceeded)).Append(": ").Append(watcher.Stats.GatherSucceeded).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_PlayerIneligible)).Append(": ").Append(watcher.Stats.GatherFailed_PlayerIneligible).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_PlayerReceivingSnapshot)).Append(": ").Append(watcher.Stats.GatherFailed_PlayerReceivingSnapshot).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_LootingIneligibleContainer)).Append(": ").Append(watcher.Stats.GatherFailed_LootingIneligibleContainer).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_ParentContainerIneligible)).Append(": ").Append(watcher.Stats.GatherFailed_ParentContainerIneligible).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_GatherPaused)).Append(": ").Append(watcher.Stats.GatherFailed_GatherPaused).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_NotAllowedToMoveItems)).Append(": ").Append(watcher.Stats.GatherFailed_NotAllowedToMoveItems).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_FoundMatchingStackInInventory)).Append(": ").Append(watcher.Stats.GatherFailed_FoundMatchingStackInInventory).AppendLine();
sb.Append("- ").Append(nameof(GatherModeStats.GatherFailed_BackpackRejectedOrDidNotCareAboutItem)).Append(": ").Append(watcher.Stats.GatherFailed_BackpackRejectedOrDidNotCareAboutItem).AppendLine();
}
}
player.Reply(sb.ToString());
}
#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)
{
if (Enum.TryParse(itemName, ignoreCase: true, result: out T 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;
return int.TryParse(arg, out var pageIndex)
? Math.Max(0, pageIndex - 1)
: -1;
}
private static bool HasItemMod<T>(ItemDefinition itemDefinition, out T itemModOfType) where T : ItemMod
{
foreach (var itemMod in itemDefinition.itemMods)
{
itemModOfType = itemMod as T;
if (itemModOfType is not null)
return true;
}
itemModOfType = null;
return false;
}
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 RidableHorse)?.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 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 = 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 BasePlaye