C#:
// Все права принадлежат дискорд сообществу https://discord.gg/VgNHPpNrz6
// #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.4")]
[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 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
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(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));
}
}
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.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 (!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, GetGatherModeDisplayOptio
// ... [Code preview truncated due to length. Full source in attached file] ...