Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of InventoryLink v1.0.5
plugins\InventoryLink\InventoryLink.dll
Decompiled 2 weeks agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("InventoryLink")] [assembly: AssemblyFileVersion("1.0.5")] [assembly: AssemblyCompany("R4V9N1")] [assembly: AssemblyDescription("Created by R4V9N1")] [assembly: AssemblyProduct("InventoryLink")] [assembly: AssemblyCopyright("Created by R4V9N1")] [assembly: AssemblyMetadata("Creator", "Created by R4V9N1")] [assembly: AssemblyVersion("1.0.5.0")] namespace InventoryLink; [BepInPlugin("r4v9n1.inventorylink", "InventoryLink", "1.0.5")] public sealed class InventoryLinkPlugin : BaseUnityPlugin { internal sealed class RequirementCheckContext { public readonly Player Player; public readonly Inventory PlayerInventory; public readonly bool CountAmounts; public readonly bool CountAny; public RequirementCheckContext(Player player, Inventory playerInventory, bool countAmounts, bool countAny) { Player = player; PlayerInventory = playerInventory; CountAmounts = countAmounts; CountAny = countAny; } } internal sealed class RequirementDisplayContext { public readonly Player Player; public readonly Inventory PlayerInventory; public RequirementDisplayContext(Player player, Inventory playerInventory) { Player = player; PlayerInventory = playerInventory; } } internal sealed class PullPlan { public readonly Player Player; public readonly List<PullItem> Items = new List<PullItem>(); public bool HasItems => Items.Count > 0; public PullPlan(Player player) { Player = player; } public void Add(string name, int quality, int amount) { for (int i = 0; i < Items.Count; i++) { if (Items[i].Name == name && Items[i].Quality == quality) { Items[i].Amount += amount; return; } } Items.Add(new PullItem(name, quality, amount)); } } internal sealed class PullItem { public readonly string Name; public readonly int Quality; public int Amount; public PullItem(string name, int quality, int amount) { Name = name; Quality = quality; Amount = amount; } } private sealed class ContainerCandidate { public readonly Container Container; public readonly float DistanceSqr; public ContainerCandidate(Container container, float distanceSqr) { Container = container; DistanceSqr = distanceSqr; } } public const string PluginGuid = "r4v9n1.inventorylink"; public const string PluginName = "InventoryLink"; public const string PluginVersion = "1.0.5"; public const string CreatorCredit = "Created by R4V9N1"; private const string PreviousDefaultCraftingStationNames = "$piece_workbench,$piece_stonecutter"; private const string DefaultCraftingStationNames = "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation"; private const string LightMyFireCoalBarrelPrefab = "R4V9N1_LightMyFireCoalBarrel"; private const string LightMyFireResinBarrelPrefab = "R4V9N1_LightMyFireResinBarrel"; private static ConfigEntry<bool> _enabled; private static ConfigEntry<float> _pullRadius; private static ConfigEntry<bool> _extendCraftingStationBuildRange; private static ConfigEntry<float> _craftingStationBuildRange; private static ConfigEntry<string> _craftingStationBuildRangeNames; private static ConfigEntry<bool> _respectWards; private static ConfigEntry<bool> _respectContainerAccess; private static ConfigEntry<bool> _skipInUseContainers; private static ConfigEntry<bool> _logPulls; private static ManualLogSource _log; private static FieldInfo _containerNViewField; private static FieldInfo _inventoryGuiCraftRecipeField; private static MethodInfo _containerLoadMethod; private static float _nextContainerFailureLogTime; private static int _suppressedContainerFailures; private static RequirementCheckContext _requirementCheck; private static RequirementDisplayContext _requirementDisplay; private static Player _lastPlacedPlayer; private static int _lastPlacedFrame = -1; private static Player _craftingPlayer; private static int _craftingFrame = -1; private static readonly HashSet<Container> _registeredContainers = new HashSet<Container>(); private static readonly List<Container> _staleContainers = new List<Container>(); private static Player _cachedPlayer; private static Vector3 _cachedPlayerPosition; private static float _cachedContainerTime = -999f; private static int _containerRegistryRevision; private static bool _containerRegistryBootstrapped; private static int _cachedRegistryRevision = -1; private static List<ContainerCandidate> _cachedContainers; private const float NearbyContainerCacheSeconds = 0.2f; private const float NearbyContainerCacheMoveSqr = 1f; private Harmony _harmony; private void Awake() { //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown _log = ((BaseUnityPlugin)this).Logger; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable InventoryLink."); _pullRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PullRadius", 50f, "How far from the player to search for linked containers."); _extendCraftingStationBuildRange = ((BaseUnityPlugin)this).Config.Bind<bool>("Crafting Stations", "ExtendBuildRange", true, "Extend configured crafting station build radii."); _craftingStationBuildRange = ((BaseUnityPlugin)this).Config.Bind<float>("Crafting Stations", "BuildRange", 50f, "Minimum build radius for configured crafting stations."); _craftingStationBuildRangeNames = ((BaseUnityPlugin)this).Config.Bind<string>("Crafting Stations", "StationNames", "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation", "Comma-separated CraftingStation m_name values to extend. Defaults to the workbench, stonecutter, forge, black forge, Galdr table, and artisan table."); _respectWards = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "RespectWards", true, "Skip containers blocked by ward/private-area access."); _respectContainerAccess = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "RespectContainerAccess", true, "Respect personal/private container access rules."); _skipInUseContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("Access", "SkipInUseContainers", true, "Do not pull from containers that another player has open."); _logPulls = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogPulls", false, "Log each successful item pull to the BepInEx log."); MigrateOldDefaults(); _containerNViewField = AccessTools.Field(typeof(Container), "m_nview"); _inventoryGuiCraftRecipeField = AccessTools.Field(typeof(InventoryGui), "m_craftRecipe"); _containerLoadMethod = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); _harmony = new Harmony("r4v9n1.inventorylink"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"InventoryLink 1.0.5 loaded."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Created by R4V9N1."); } private void OnDestroy() { if (_harmony != null) { _harmony.UnpatchSelf(); _harmony = null; } ResetContainerRegistry(); } internal static bool IsEnabledFor(Player player) { if (_enabled == null || !_enabled.Value || (Object)(object)player == (Object)null) { return false; } if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return Player.m_localPlayer == player; } return true; } internal static void ExtendCraftingStationBuildRange(CraftingStation station, ref float range) { if (_enabled != null && _enabled.Value && _extendCraftingStationBuildRange != null && _extendCraftingStationBuildRange.Value && !((Object)(object)station == (Object)null) && IsConfiguredCraftingStation(station)) { float num = ((_craftingStationBuildRange == null) ? 50f : Math.Max(0f, _craftingStationBuildRange.Value)); if (range < num) { range = num; } } } internal static bool BeginRequirementCheck(Player player, RequirementMode mode) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 if (!IsEnabledFor(player)) { return false; } if ((int)mode != 0 && (int)mode != 2) { return false; } _requirementCheck = new RequirementCheckContext(player, ((Humanoid)player).GetInventory(), (int)mode == 0, (int)mode == 2); return true; } internal static void EndRequirementCheck(bool entered) { if (entered) { _requirementCheck = null; } } internal static bool BeginRecipeRequirementCheck(Player player, Recipe recipe, bool discover) { if (!IsEnabledFor(player) || discover || !SupportsRecipeAutoPull(recipe)) { return false; } _requirementCheck = new RequirementCheckContext(player, ((Humanoid)player).GetInventory(), countAmounts: true, countAny: false); return true; } internal static bool BeginRequirementDisplay(Player player) { if (!IsEnabledFor(player)) { return false; } _requirementDisplay = new RequirementDisplayContext(player, ((Humanoid)player).GetInventory()); return true; } internal static void EndRequirementDisplay(bool entered) { if (entered) { _requirementDisplay = null; } } internal static bool BeginCrafting(InventoryGui inventoryGui, Player player) { if (!IsEnabledFor(player)) { return false; } if (!SupportsRecipeAutoPull(GetCraftRecipe(inventoryGui))) { return false; } _craftingPlayer = player; _craftingFrame = Time.frameCount; return true; } internal static void EndCrafting(bool entered) { if (entered) { _craftingPlayer = null; _craftingFrame = -1; } } internal static void AddNearbyCountForRequirement(Inventory inventory, string itemName, int quality, bool matchWorldLevel, ref int count) { Player val = null; Inventory val2 = null; if (_requirementCheck != null && _requirementCheck.CountAmounts) { val = _requirementCheck.Player; val2 = _requirementCheck.PlayerInventory; } else { if (_requirementDisplay == null) { return; } val = _requirementDisplay.Player; val2 = _requirementDisplay.PlayerInventory; } if (inventory == val2) { count += CountNearbyItem(val, itemName, quality, matchWorldLevel, int.MaxValue); } } internal static void AddNearbyHaveItemForRequirement(Inventory inventory, string itemName, bool matchWorldLevel, ref bool haveItem) { if (!haveItem && _requirementCheck != null && _requirementCheck.CountAny && inventory == _requirementCheck.PlayerInventory) { haveItem = CountNearbyItem(_requirementCheck.Player, itemName, -1, matchWorldLevel, 1) > 0; } } internal static void MarkPlaced(Player player, bool placed) { if (placed && IsEnabledFor(player)) { _lastPlacedPlayer = player; _lastPlacedFrame = Time.frameCount; } } internal static PullPlan CreatePullPlan(Player player, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier) { bool flag = _lastPlacedPlayer == player && _lastPlacedFrame == Time.frameCount; bool flag2 = _craftingPlayer == player && _craftingFrame == Time.frameCount; if (!IsEnabledFor(player) || (!flag && !flag2)) { return null; } if (requirements == null || requirements.Length == 0) { return null; } Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory == null) { return null; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); PullPlan pullPlan = new PullPlan(player); foreach (Requirement val in requirements) { if (val != null && !((Object)(object)val.m_resItem == (Object)null) && (!((Object)(object)currentCraftingStation != (Object)null) || currentCraftingStation.m_upgrader == val.m_upgraderResource) && (!((Object)(object)currentCraftingStation == (Object)null) || !val.m_upgraderResource)) { int num = val.GetAmount(qualityLevel) * multiplier; if (num > 0) { string name = val.m_resItem.m_itemData.m_shared.m_name; pullPlan.Add(name, itemQuality, num); } } } PullPlan pullPlan2 = new PullPlan(player); for (int j = 0; j < pullPlan.Items.Count; j++) { PullItem pullItem = pullPlan.Items[j]; int num2 = inventory.CountItems(pullItem.Name, pullItem.Quality, true); int num3 = pullItem.Amount - num2; if (num3 > 0) { pullPlan2.Add(pullItem.Name, pullItem.Quality, num3); } } if (!pullPlan2.HasItems) { return null; } return pullPlan2; } internal static void PayPullPlan(PullPlan plan) { if (plan == null || !IsEnabledFor(plan.Player)) { return; } List<ContainerCandidate> list = FindContainers(plan.Player); long playerID = plan.Player.GetPlayerID(); for (int i = 0; i < plan.Items.Count; i++) { PullItem pullItem = plan.Items[i]; int num = pullItem.Amount; for (int j = 0; j < list.Count; j++) { if (num <= 0) { break; } Container container = list[j].Container; if (CanUseContainer(container, playerID)) { int num2 = Math.Max(0, num); int num3 = PullFromContainer(container, pullItem.Name, pullItem.Quality, num2); if (num3 < 0 || num3 > num2) { throw new InvalidOperationException("InventoryLink payment invariant violated: removal exceeded remaining recipe budget."); } num -= num3; } } if (num > 0 && _log != null) { _log.LogWarning((object)("InventoryLink could not pull " + num + "x " + pullItem.Name + " after placement. The container contents may have changed.")); } } ClearContainerCache(); } private static int CountNearbyItem(Player player, string itemName, int quality, bool matchWorldLevel, int stopAt) { if (string.IsNullOrEmpty(itemName) || !IsEnabledFor(player)) { return 0; } int num = 0; long playerID = player.GetPlayerID(); List<ContainerCandidate> list = FindContainers(player); for (int i = 0; i < list.Count; i++) { Container container = list[i].Container; if (!CanUseContainer(container, playerID)) { continue; } Inventory loadedInventory = GetLoadedInventory(container); if (loadedInventory != null) { num += loadedInventory.CountItems(itemName, quality, matchWorldLevel); if (num >= stopAt) { return num; } } } return num; } private static int PullFromContainer(Container container, string itemName, int quality, int amount) { if ((Object)(object)container == (Object)null || amount <= 0) { return 0; } ZNetView containerView = GetContainerView(container); if ((Object)(object)containerView != (Object)null && containerView.IsValid() && !containerView.IsOwner()) { containerView.ClaimOwnership(); } Inventory loadedInventory = GetLoadedInventory(container); if (loadedInventory == null) { return 0; } int num = loadedInventory.CountItems(itemName, quality, true); int num2 = Math.Min(num, amount); if (num2 <= 0) { return 0; } loadedInventory.RemoveItem(itemName, num2, quality, true); int num3 = loadedInventory.CountItems(itemName, quality, true); int num4 = num - num3; if (num4 > 0 && _log != null && _logPulls != null && _logPulls.Value) { _log.LogInfo((object)("Pulled " + num4 + "x " + itemName + " from " + container.GetHoverName() + ".")); } return Math.Max(num4, 0); } internal static void RegisterContainer(Container container) { if (!((Object)(object)container == (Object)null) && _registeredContainers.Add(container)) { _containerRegistryRevision++; ClearContainerCache(); } } private static void BootstrapContainerRegistry() { if (!_containerRegistryBootstrapped) { Container[] array = Object.FindObjectsByType<Container>((FindObjectsSortMode)0); _containerRegistryBootstrapped = true; for (int i = 0; i < array.Length; i++) { RegisterContainer(array[i]); } } } private static void PruneStaleContainers() { _staleContainers.Clear(); foreach (Container registeredContainer in _registeredContainers) { if ((Object)(object)registeredContainer == (Object)null) { _staleContainers.Add(registeredContainer); } } if (_staleContainers.Count != 0) { for (int i = 0; i < _staleContainers.Count; i++) { _registeredContainers.Remove(_staleContainers[i]); } _staleContainers.Clear(); _containerRegistryRevision++; ClearContainerCache(); } } private static List<ContainerCandidate> FindContainers(Player player) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) List<ContainerCandidate> result = new List<ContainerCandidate>(); if (!IsEnabledFor(player)) { return result; } BootstrapContainerRegistry(); PruneStaleContainers(); Vector3 position = ((Component)player).transform.position; float unscaledTime = Time.unscaledTime; Vector3 val; if (_cachedContainers != null && _cachedPlayer == player && _cachedRegistryRevision == _containerRegistryRevision && unscaledTime - _cachedContainerTime >= 0f && unscaledTime - _cachedContainerTime <= 0.2f) { val = position - _cachedPlayerPosition; if (((Vector3)(ref val)).sqrMagnitude <= 1f) { return _cachedContainers; } } List<ContainerCandidate> list = new List<ContainerCandidate>(); float num = ((_pullRadius == null) ? 50f : Math.Max(0f, _pullRadius.Value)); float num2 = num * num; foreach (Container registeredContainer in _registeredContainers) { if (!((Object)(object)registeredContainer == (Object)null) && !IsReservedAutomationContainer(registeredContainer)) { float sqrMagnitude; try { val = ((Component)registeredContainer).transform.position - position; sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; } catch (Exception exception) { LogUnexpectedContainerFailure(registeredContainer, exception); continue; } if (!(sqrMagnitude > num2)) { list.Add(new ContainerCandidate(registeredContainer, sqrMagnitude)); } } } list.Sort(delegate(ContainerCandidate left, ContainerCandidate right) { float distanceSqr = left.DistanceSqr; return distanceSqr.CompareTo(right.DistanceSqr); }); _cachedPlayer = player; _cachedPlayerPosition = position; _cachedContainerTime = unscaledTime; _cachedRegistryRevision = _containerRegistryRevision; _cachedContainers = list; return list; } private static bool CanUseContainer(Container container, long playerId) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) try { if (IsReservedAutomationContainer(container)) { return false; } if (!TryGetReadyContainer(container, out var _)) { return false; } if (_skipInUseContainers != null && _skipInUseContainers.Value && container.IsInUse()) { return false; } if (_respectWards != null && _respectWards.Value && ContainerChecksGuardStone(container) && !PrivateArea.CheckAccess(((Component)container).transform.position, 0f, true, false)) { return false; } if (_respectContainerAccess != null && _respectContainerAccess.Value && !CheckContainerAccess(container, playerId)) { return false; } return GetLoadedInventory(container) != null; } catch (Exception exception) { LogUnexpectedContainerFailure(container, exception); return false; } } private static bool IsReservedAutomationContainer(Container container) { if ((Object)(object)container == (Object)null || (Object)(object)((Component)container).gameObject == (Object)null) { return false; } string text = ((Object)((Component)container).gameObject).name ?? string.Empty; if (!text.StartsWith("R4V9N1_LightMyFireCoalBarrel", StringComparison.Ordinal)) { return text.StartsWith("R4V9N1_LightMyFireResinBarrel", StringComparison.Ordinal); } return true; } private static bool ContainerChecksGuardStone(Container container) { if ((Object)(object)container != (Object)null) { return container.m_checkGuardStone; } return false; } private static bool CheckContainerAccess(Container container, long playerId) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown if ((Object)(object)container == (Object)null) { return false; } PrivacySetting privacy = container.m_privacy; switch ((int)privacy) { case 2: return true; case 0: { Piece component = ((Component)container).GetComponent<Piece>(); if ((Object)(object)component != (Object)null) { return component.GetCreator() == playerId; } return false; } default: return false; } } private static Inventory GetLoadedInventory(Container container) { try { if (!TryGetReadyContainer(container, out var view)) { return null; } LoadContainer(container); if (!view.IsValid()) { return null; } return container.GetInventory(); } catch (Exception exception) { LogUnexpectedContainerFailure(container, exception); return null; } } private static void LoadContainer(Container container) { if (_containerLoadMethod != null) { _containerLoadMethod.Invoke(container, null); } } private static ZNetView GetContainerView(Container container) { if (_containerNViewField == null || (Object)(object)container == (Object)null) { return null; } object? value = _containerNViewField.GetValue(container); return (ZNetView)((value is ZNetView) ? value : null); } private static bool TryGetReadyContainer(Container container, out ZNetView view) { view = null; if ((Object)(object)container == (Object)null || !((Behaviour)container).isActiveAndEnabled) { return false; } view = GetContainerView(container); if ((Object)(object)view != (Object)null && view.IsValid()) { return container.GetInventory() != null; } return false; } private static void LogUnexpectedContainerFailure(Container container, Exception exception) { if (_log != null && exception != null) { while (exception is TargetInvocationException && exception.InnerException != null) { exception = exception.InnerException; } float unscaledTime = Time.unscaledTime; if (unscaledTime < _nextContainerFailureLogTime) { _suppressedContainerFailures++; return; } string text = (((Object)(object)container == (Object)null) ? "<destroyed>" : ((Object)((Component)container).gameObject).name); string text2 = ((_suppressedContainerFailures > 0) ? (" (" + _suppressedContainerFailures + " similar failures suppressed)") : ""); _suppressedContainerFailures = 0; _nextContainerFailureLogTime = unscaledTime + 5f; _log.LogDebug((object)("Skipped unavailable container '" + text + "': " + exception.GetType().Name + ": " + exception.Message + text2)); } } private static Recipe GetCraftRecipe(InventoryGui inventoryGui) { if (_inventoryGuiCraftRecipeField == null || (Object)(object)inventoryGui == (Object)null) { return null; } object? value = _inventoryGuiCraftRecipeField.GetValue(inventoryGui); return (Recipe)((value is Recipe) ? value : null); } private static bool SupportsRecipeAutoPull(Recipe recipe) { if ((Object)(object)recipe != (Object)null) { return !recipe.m_requireOnlyOneIngredient; } return false; } private static bool IsConfiguredCraftingStation(CraftingStation station) { if ((Object)(object)station == (Object)null) { return false; } string text = ((_craftingStationBuildRangeNames == null) ? "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation" : _craftingStationBuildRangeNames.Value); if (string.IsNullOrEmpty(text)) { return false; } string a = station.m_name ?? ""; string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (text2.Length != 0 && string.Equals(a, text2, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private void MigrateOldDefaults() { bool flag = false; if (_pullRadius != null && Math.Abs(_pullRadius.Value - 30f) < 0.001f) { _pullRadius.Value = 50f; flag = true; } if (_craftingStationBuildRangeNames != null && string.Equals(_craftingStationBuildRangeNames.Value, "$piece_workbench,$piece_stonecutter", StringComparison.Ordinal)) { _craftingStationBuildRangeNames.Value = "$piece_workbench,$piece_stonecutter,$piece_forge,$piece_blackforge,$piece_magetable,$piece_artisanstation"; flag = true; } if (flag) { ((BaseUnityPlugin)this).Config.Save(); } } private static void ClearContainerCache() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) _cachedPlayer = null; _cachedPlayerPosition = Vector3.zero; _cachedContainerTime = -999f; _cachedRegistryRevision = -1; _cachedContainers = null; } private static void ResetContainerRegistry() { _registeredContainers.Clear(); _staleContainers.Clear(); _containerRegistryRevision = 0; _containerRegistryBootstrapped = false; ClearContainerCache(); } } [HarmonyPatch(typeof(Container), "Awake")] internal static class InventoryLinkContainerAwakePatch { private static void Postfix(Container __instance) { InventoryLinkPlugin.RegisterContainer(__instance); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] internal static class PlayerHaveRequirementsPatch { private static void Prefix(Player __instance, RequirementMode mode, ref bool __state) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) __state = InventoryLinkPlugin.BeginRequirementCheck(__instance, mode); } private static void Postfix(bool __state) { InventoryLinkPlugin.EndRequirementCheck(__state); } private static Exception Finalizer(Exception __exception, bool __state) { InventoryLinkPlugin.EndRequirementCheck(__state); return __exception; } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Recipe), typeof(bool), typeof(int), typeof(int) })] internal static class PlayerHaveRecipeRequirementsPatch { private static void Prefix(Player __instance, Recipe recipe, bool discover, ref bool __state) { __state = InventoryLinkPlugin.BeginRecipeRequirementCheck(__instance, recipe, discover); } private static void Postfix(bool __state) { InventoryLinkPlugin.EndRequirementCheck(__state); } private static Exception Finalizer(Exception __exception, bool __state) { InventoryLinkPlugin.EndRequirementCheck(__state); return __exception; } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement", new Type[] { typeof(Transform), typeof(Requirement), typeof(Player), typeof(bool), typeof(int), typeof(int) })] internal static class InventoryGuiSetupRequirementPatch { private static void Prefix(Player player, ref bool __state) { __state = InventoryLinkPlugin.BeginRequirementDisplay(player); } private static void Postfix(bool __state) { InventoryLinkPlugin.EndRequirementDisplay(__state); } private static Exception Finalizer(Exception __exception, bool __state) { InventoryLinkPlugin.EndRequirementDisplay(__state); return __exception; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting", new Type[] { typeof(Player) })] internal static class InventoryGuiDoCraftingPatch { private static void Prefix(InventoryGui __instance, Player player, ref bool __state) { __state = InventoryLinkPlugin.BeginCrafting(__instance, player); } private static void Postfix(bool __state) { InventoryLinkPlugin.EndCrafting(__state); } private static Exception Finalizer(Exception __exception, bool __state) { InventoryLinkPlugin.EndCrafting(__state); return __exception; } } [HarmonyPatch(typeof(Inventory), "CountItems", new Type[] { typeof(string), typeof(int), typeof(bool) })] internal static class InventoryCountItemsPatch { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { InventoryLinkPlugin.AddNearbyCountForRequirement(__instance, name, quality, matchWorldLevel, ref __result); } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] internal static class InventoryHaveItemPatch { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { InventoryLinkPlugin.AddNearbyHaveItemForRequirement(__instance, name, matchWorldLevel, ref __result); } } [HarmonyPatch(typeof(CraftingStation), "GetStationBuildRange")] internal static class CraftingStationGetStationBuildRangePatch { private static void Postfix(CraftingStation __instance, ref float __result) { InventoryLinkPlugin.ExtendCraftingStationBuildRange(__instance, ref __result); } } [HarmonyPatch(typeof(Player), "TryPlacePiece", new Type[] { typeof(Piece) })] internal static class PlayerTryPlacePiecePatch { private static void Postfix(Player __instance, bool __result) { InventoryLinkPlugin.MarkPlaced(__instance, __result); } } [HarmonyPatch(typeof(Player), "ConsumeResources", new Type[] { typeof(Requirement[]), typeof(int), typeof(int), typeof(int) })] internal static class PlayerConsumeResourcesPatch { private static void Prefix(Player __instance, Requirement[] requirements, int qualityLevel, int itemQuality, int multiplier, ref InventoryLinkPlugin.PullPlan __state) { __state = InventoryLinkPlugin.CreatePullPlan(__instance, requirements, qualityLevel, itemQuality, multiplier); } private static void Postfix(InventoryLinkPlugin.PullPlan __state) { InventoryLinkPlugin.PayPullPlan(__state); } } [BepInPlugin("r4v9n1.inventorylink.autosort", "InventoryLink Auto Sort", "0.1.1")] public sealed class InventorySortPlugin : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__17_0; public static UnityAction <>9__17_1; internal void <SetupButtons>b__17_0() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { SortInventory(((Humanoid)localPlayer).GetInventory()); } } internal void <SetupButtons>b__17_1() { InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null) ? GetCurrentContainer(instance) : null); if ((Object)(object)val != (Object)null) { SortInventory(val.GetInventory()); } } } public const string PluginGuid = "r4v9n1.inventorylink.autosort"; public const string PluginName = "InventoryLink Auto Sort"; public const string PluginVersion = "0.1.1"; private static readonly Dictionary<ItemType, int> CategoryOrder = new Dictionary<ItemType, int> { { (ItemType)3, 0 }, { (ItemType)14, 0 }, { (ItemType)22, 0 }, { (ItemType)4, 0 }, { (ItemType)20, 0 }, { (ItemType)5, 1 }, { (ItemType)6, 2 }, { (ItemType)7, 2 }, { (ItemType)11, 2 }, { (ItemType)17, 2 }, { (ItemType)12, 2 }, { (ItemType)19, 3 }, { (ItemType)18, 3 }, { (ItemType)15, 3 }, { (ItemType)9, 4 }, { (ItemType)23, 4 }, { (ItemType)2, 5 }, { (ItemType)21, 5 }, { (ItemType)13, 6 }, { (ItemType)10, 7 }, { (ItemType)24, 7 }, { (ItemType)1, 8 }, { (ItemType)16, 9 }, { (ItemType)0, 9 } }; private static ConfigEntry<bool> _enabled; private static ConfigEntry<bool> _addSortButtons; private static ConfigEntry<KeyboardShortcut> _sortKey; private static ConfigEntry<float> _playerButtonOffsetY; private static ConfigEntry<float> _containerButtonOffsetY; private static ManualLogSource _log; private static Button _playerSortButton; private static Button _containerSortButton; private static FieldInfo _currentContainerField; private static MethodInfo _inventoryChangedMethod; private static bool _apiReady; private void Awake() { //IL_0139: Unknown result type (might be due to invalid IL or missing references) _log = ((BaseUnityPlugin)this).Logger; _currentContainerField = AccessTools.Field(typeof(InventoryGui), "m_currentContainer"); _inventoryChangedMethod = AccessTools.Method(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); if (_currentContainerField == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Auto Sort disabled: InventoryGui.m_currentContainer was not found."); } if (_inventoryChangedMethod == null || _inventoryChangedMethod.ReturnType != typeof(void)) { _inventoryChangedMethod = null; ((BaseUnityPlugin)this).Logger.LogError((object)"Auto Sort disabled: Inventory.Changed(bool, bool) was not found with the required signature."); } _apiReady = _currentContainerField != null && _inventoryChangedMethod != null; _enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable auto-sort for the player inventory and open containers."); _addSortButtons = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AddSortButtons", true, "Clone vanilla buttons into 'Sort' buttons on the inventory/container panels."); _sortKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "SortKey", new KeyboardShortcut((KeyCode)120, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Key combo that sorts the player inventory, and the open container if any."); _playerButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("General", "PlayerButtonOffsetY", 35f, "Vertical offset (UI units) of the cloned player-inventory sort button from the Stack All button it's cloned from."); _containerButtonOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ContainerButtonOffsetY", 35f, "Vertical offset (UI units) of the cloned container sort button from the Take All button it's cloned from."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"InventoryLink Auto Sort 0.1.1 loaded."); } private void Update() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!_apiReady || _enabled == null || !_enabled.Value || _sortKey == null) { return; } KeyboardShortcut value = _sortKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { SortInventory(((Humanoid)localPlayer).GetInventory()); } InventoryGui instance = InventoryGui.instance; Container val = (((Object)(object)instance != (Object)null && instance.IsContainerOpen()) ? GetCurrentContainer(instance) : null); if ((Object)(object)val != (Object)null) { SortInventory(val.GetInventory()); } } } internal static void SetupButtons(InventoryGui gui) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown if (!_apiReady || _addSortButtons == null || !_addSortButtons.Value || (Object)(object)gui == (Object)null) { return; } if ((Object)(object)_playerSortButton == (Object)null) { if ((Object)(object)gui.m_stackAllButton == (Object)null) { if (_log != null) { _log.LogWarning((object)"Auto Sort: InventoryGui.m_stackAllButton was null; player sort button was not created."); } } else { float offsetY = ((_playerButtonOffsetY == null) ? 35f : _playerButtonOffsetY.Value); Button stackAllButton = gui.m_stackAllButton; object obj = <>c.<>9__17_0; if (obj == null) { UnityAction val = delegate { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { SortInventory(((Humanoid)localPlayer).GetInventory()); } }; <>c.<>9__17_0 = val; obj = (object)val; } _playerSortButton = CloneButton(stackAllButton, offsetY, "Inv. Sort", "PlayerSortButton", (UnityAction)obj); } } if (!((Object)(object)_containerSortButton == (Object)null)) { return; } if ((Object)(object)gui.m_takeAllButton == (Object)null) { if (_log != null) { _log.LogWarning((object)"Auto Sort: InventoryGui.m_takeAllButton was null; container sort button was not created."); } return; } float offsetY2 = ((_containerButtonOffsetY == null) ? 35f : _containerButtonOffsetY.Value); Button takeAllButton = gui.m_takeAllButton; object obj2 = <>c.<>9__17_1; if (obj2 == null) { UnityAction val2 = delegate { InventoryGui instance = InventoryGui.instance; Container val3 = (((Object)(object)instance != (Object)null) ? GetCurrentContainer(instance) : null); if ((Object)(object)val3 != (Object)null) { SortInventory(val3.GetInventory()); } }; <>c.<>9__17_1 = val2; obj2 = (object)val2; } _containerSortButton = CloneButton(takeAllButton, offsetY2, "Sort", "ContainerSortButton", (UnityAction)obj2); ((Component)_containerSortButton).gameObject.SetActive(false); } private static Container GetCurrentContainer(InventoryGui gui) { if (_currentContainerField == null || (Object)(object)gui == (Object)null) { return null; } object? value = _currentContainerField.GetValue(gui); return (Container)((value is Container) ? value : null); } internal static void SetContainerButtonVisible(bool visible) { if ((Object)(object)_containerSortButton != (Object)null) { ((Component)_containerSortButton).gameObject.SetActive(visible); } } private unsafe static Button CloneButton(Button template, float offsetY, string label, string cloneName, UnityAction onClick) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, ((Component)template).transform.parent); ((Object)val).name = cloneName; val.SetActive(true); LayoutElement val2 = val.GetComponent<LayoutElement>(); if ((Object)(object)val2 == (Object)null) { val2 = val.AddComponent<LayoutElement>(); } val2.ignoreLayout = true; RectTransform component = val.GetComponent<RectTransform>(); if ((Object)(object)component != (Object)null) { component.anchoredPosition += new Vector2(0f, offsetY); } TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = label; } Button component2 = val.GetComponent<Button>(); ((UnityEventBase)component2.onClick).RemoveAllListeners(); ((UnityEvent)component2.onClick).AddListener(onClick); if (_log != null) { Vector2 val3 = (((Object)(object)component != (Object)null) ? component.anchoredPosition : Vector2.zero); ManualLogSource log = _log; string[] obj = new string[7] { "Auto Sort: created '", cloneName, "' at ", null, null, null, null }; Vector2 val4 = val3; obj[3] = ((object)(*(Vector2*)(&val4))/*cast due to .constrained prefix*/).ToString(); obj[4] = " (parent active="; obj[5] = ((Component)val.transform.parent).gameObject.activeInHierarchy.ToString(); obj[6] = ")."; log.LogInfo((object)string.Concat(obj)); } return component2; } internal static void SortInventory(Inventory inventory) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) if (!_apiReady || inventory == null) { return; } int width = inventory.GetWidth(); int height = inventory.GetHeight(); if (width <= 0 || height <= 0) { return; } List<ItemData> allItems = inventory.GetAllItems(); List<ItemData> list = new List<ItemData>(); HashSet<int> hashSet = new HashSet<int>(); for (int i = 0; i < allItems.Count; i++) { ItemData val = allItems[i]; if (val != null && val.IsEquipable()) { hashSet.Add(val.m_gridPos.y * width + val.m_gridPos.x); } else if (val != null) { list.Add(val); } } list.Sort(CompareItems); int num = 0; for (int j = 0; j < height; j++) { if (num >= list.Count) { break; } for (int k = 0; k < width; k++) { if (num >= list.Count) { break; } if (!hashSet.Contains(j * width + k)) { list[num].m_gridPos = new Vector2i(k, j); num++; } } } if (_inventoryChangedMethod != null) { _inventoryChangedMethod.Invoke(inventory, new object[2] { false, false }); } if (_log != null) { _log.LogInfo((object)("Sorted " + num + " item stack(s), left " + hashSet.Count + " equippable item(s) in place.")); } } private static int CompareItems(ItemData a, ItemData b) { int num = GetCategoryOrder(a).CompareTo(GetCategoryOrder(b)); if (num != 0) { return num; } string strA = ((a.m_shared != null) ? a.m_shared.m_name : string.Empty); string strB = ((b.m_shared != null) ? b.m_shared.m_name : string.Empty); int num2 = string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase); if (num2 != 0) { return num2; } int num3 = b.m_quality.CompareTo(a.m_quality); if (num3 != 0) { return num3; } return b.m_stack.CompareTo(a.m_stack); } private static int GetCategoryOrder(ItemData item) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (item != null && item.m_shared != null && CategoryOrder.TryGetValue(item.m_shared.m_itemType, out var value)) { return value; } return 9; } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGuiAwakeSortPatch { private static void Postfix(InventoryGui __instance) { InventorySortPlugin.SetupButtons(__instance); } } [HarmonyPatch(typeof(InventoryGui), "Show", new Type[] { typeof(Container), typeof(int) })] internal static class InventoryGuiShowSortPatch { private static void Postfix() { InventorySortPlugin.SetContainerButtonVisible(visible: true); } } [HarmonyPatch(typeof(InventoryGui), "CloseContainer")] internal static class InventoryGuiCloseContainerSortPatch { private static void Postfix() { InventorySortPlugin.SetContainerButtonVisible(visible: false); } }