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 InventorySort v1.2.0
InventorySort.dll
Decompiled 2 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("InventorySort")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("InventorySort")] [assembly: AssemblyTitle("InventorySort")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace InventorySort { internal static class InventorySorter { private const int HotbarRow = 0; public static bool CanSortNow() { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if ((Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible()) { return false; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return false; } if (Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible()) { return false; } if ((Object)(object)InventoryGui.instance.m_dragGo != (Object)null) { return false; } return true; } public static void SortContainer(Container container) { if ((Object)(object)container == (Object)null) { return; } if ((Object)(object)container.m_nview == (Object)null || !container.m_nview.IsValid() || !container.m_nview.IsOwner()) { InventorySortPlugin.Log.LogDebug((object)"Container not owned by local player, skipping sort"); return; } Sort(container.GetInventory(), InventorySortPlugin.ContainerFill.Value, (Vector2i _) => false); } public static void SortPlayer() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return; } bool skipHotbar = InventorySortPlugin.SkipHotbar.Value; HashSet<(int, int)> locked = (InventorySortPlugin.RespectQuickStackLocks.Value ? QuickStackLocks.LoadFor(localPlayer) : new HashSet<(int, int)>()); Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory != null && InventorySortPlugin.IgnoreEquipped.Value) { foreach (ItemData item in inventory.m_inventory) { if (item.m_equipped) { locked.Add((item.m_gridPos.x, item.m_gridPos.y)); } } } Sort(inventory, InventorySortPlugin.PlayerFill.Value, (Vector2i pos) => (skipHotbar && pos.y == 0) || locked.Contains((pos.x, pos.y))); } private static void Sort(Inventory inventory, FillStart fill, Func<Vector2i, bool> isReserved) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) if (inventory == null) { return; } List<ItemData> inventory2 = inventory.m_inventory; List<ItemData> list = inventory2.Where((ItemData i) => !isReserved(i.m_gridPos)).ToList(); if (list.Count == 0) { return; } if (InventorySortPlugin.MergeStacks.Value) { Merge(list, inventory2); } List<Vector2i> list2 = BuildSlotOrder(inventory.GetWidth(), inventory.GetHeight(), fill, isReserved); List<ItemData> list3 = Order(list); if (list3.Count > list2.Count) { InventorySortPlugin.Log.LogWarning((object)$"Not enough slots to sort ({list3.Count} items, {list2.Count} slots); leaving layout as is"); inventory.Changed(false, false); return; } for (int num = 0; num < list3.Count; num++) { list3[num].m_gridPos = list2[num]; } inventory.Changed(false, false); } private static List<Vector2i> BuildSlotOrder(int width, int height, FillStart fill, Func<Vector2i, bool> isReserved) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) List<Vector2i> list = new List<Vector2i>(width * height); Vector2i val = default(Vector2i); for (int i = 0; i < height; i++) { int num = ((fill == FillStart.Top) ? i : (height - 1 - i)); for (int j = 0; j < width; j++) { ((Vector2i)(ref val))..ctor(j, num); if (!isReserved(val)) { list.Add(val); } } } return list; } private static void Merge(List<ItemData> movable, List<ItemData> all) { foreach (IGrouping<string, ItemData> item in movable.Where(IsMergeable).GroupBy(StackKey).ToList()) { List<ItemData> list = item.OrderByDescending((ItemData i) => i.m_stack).ToList(); if (list.Count < 2) { continue; } int num = list.Sum((ItemData i) => i.m_stack); int maxStackSize = list[0].m_shared.m_maxStackSize; foreach (ItemData item2 in list) { int num2 = Mathf.Min(maxStackSize, num); num -= num2; if (num2 > 0) { item2.m_stack = num2; continue; } movable.Remove(item2); all.Remove(item2); } } } private static bool IsMergeable(ItemData item) { if (item.m_shared.m_maxStackSize > 1) { if (item.m_customData != null) { return item.m_customData.Count == 0; } return true; } return false; } private static string StackKey(ItemData item) { return $"{item.m_shared.m_name}|{item.m_quality}|{item.m_variant}|{item.m_worldLevel}"; } private static List<ItemData> Order(List<ItemData> items) { StringComparer currentCultureIgnoreCase = StringComparer.CurrentCultureIgnoreCase; var source = from i in items group i by i.m_shared.m_name into g select new { Name = LocalizedName(g.Key), Total = g.Sum((ItemData i) => i.m_stack), Items = (from i in g orderby i.m_quality descending, i.m_stack descending, i.m_durability descending select i).ToList() }; var list = ((InventorySortPlugin.Mode.Value == SortMode.Quantity) ? source.OrderByDescending(t => t.Total).ThenBy(t => t.Name, currentCultureIgnoreCase).ToList() : source.OrderBy(t => t.Name, currentCultureIgnoreCase).ToList()); if (InventorySortPlugin.Reverse.Value) { list.Reverse(); } return list.SelectMany(t => t.Items).ToList(); } private static string LocalizedName(string token) { if (Localization.instance == null) { return token; } return Localization.instance.Localize(token); } } public enum FillStart { Bottom, Top } public enum SortMode { Alphabetical, Quantity } [BepInPlugin("dreamscapist.valheim.inventorysort", "InventorySort", "1.2.0")] public class InventorySortPlugin : BaseUnityPlugin { public const string PluginGUID = "dreamscapist.valheim.inventorysort"; public const string PluginName = "InventorySort"; public const string PluginVersion = "1.2.0"; internal static ManualLogSource Log; internal static ConfigEntry<KeyboardShortcut> SortKey; internal static ConfigEntry<bool> KeySortsPlayerWhenNoContainer; internal static ConfigEntry<SortMode> Mode; internal static ConfigEntry<bool> Reverse; internal static ConfigEntry<bool> MergeStacks; internal static ConfigEntry<FillStart> ContainerFill; internal static ConfigEntry<FillStart> PlayerFill; internal static ConfigEntry<bool> SkipHotbar; internal static ConfigEntry<bool> RespectQuickStackLocks; internal static ConfigEntry<bool> IgnoreEquipped; internal static ConfigEntry<bool> ShowButtons; internal static ConfigEntry<float> ContainerButtonX; internal static ConfigEntry<float> ContainerButtonY; internal static ConfigEntry<float> PlayerButtonX; internal static ConfigEntry<float> PlayerButtonY; private Harmony _harmony; private void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; SortKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "SortKey", new KeyboardShortcut((KeyCode)116, Array.Empty<KeyCode>()), "Sorts the open storage while the inventory is open."); KeySortsPlayerWhenNoContainer = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "KeySortsPlayerWhenNoContainer", true, "If no storage is open, the sort key sorts your own inventory instead."); Mode = ((BaseUnityPlugin)this).Config.Bind<SortMode>("2 - Sorting", "SortMode", SortMode.Alphabetical, "Alphabetical = by item name. Quantity = item types you have the most of come first."); Reverse = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Sorting", "ReverseOrder", false, "Reverses the order (Z→A for alphabetical, fewest first for quantity)."); MergeStacks = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Sorting", "MergeStacks", true, "Combine partial stacks of the same item while sorting."); ContainerFill = ((BaseUnityPlugin)this).Config.Bind<FillStart>("2 - Sorting", "StorageFillStart", FillStart.Bottom, "Where sorted items start filling in storages. Bottom = bottom row first; Top = top row first."); PlayerFill = ((BaseUnityPlugin)this).Config.Bind<FillStart>("2 - Sorting", "PlayerFillStart", FillStart.Top, "Where sorted items start filling in your own inventory."); SkipHotbar = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Sorting", "SkipHotbar", true, "Leave the hotbar row (top row, slots 1–8) of your own inventory untouched."); RespectQuickStackLocks = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Sorting", "RespectQuickStackDepositLocks", true, "Leave inventory slots locked by QuickStackDeposit untouched. Does nothing if QuickStackDeposit isn't installed."); IgnoreEquipped = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Sorting", "IgnoreEquippedItems", true, "Leave equipped items (weapons, armor, etc.) in their current slots when sorting your inventory."); ShowButtons = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - UI", "ShowSortButtons", true, "Show the Sort buttons."); ContainerButtonX = ((BaseUnityPlugin)this).Config.Bind<float>("3 - UI", "StorageButtonOffsetX", 8f, "Horizontal offset of the storage Sort button from the storage panel's top-right corner."); ContainerButtonY = ((BaseUnityPlugin)this).Config.Bind<float>("3 - UI", "StorageButtonOffsetY", 0f, "Vertical offset of the storage Sort button from the storage panel's top-right corner."); PlayerButtonX = ((BaseUnityPlugin)this).Config.Bind<float>("3 - UI", "InventoryButtonOffsetX", 8f, "Horizontal offset of the inventory Sort button from the inventory panel's top-right corner."); PlayerButtonY = ((BaseUnityPlugin)this).Config.Bind<float>("3 - UI", "InventoryButtonOffsetY", 0f, "Vertical offset of the inventory Sort button from the inventory panel's top-right corner."); ShowButtons.SettingChanged += delegate { SortButtons.ApplyLayout(); }; ContainerButtonX.SettingChanged += delegate { SortButtons.ApplyLayout(); }; ContainerButtonY.SettingChanged += delegate { SortButtons.ApplyLayout(); }; PlayerButtonX.SettingChanged += delegate { SortButtons.ApplyLayout(); }; PlayerButtonY.SettingChanged += delegate { SortButtons.ApplyLayout(); }; _harmony = new Harmony("dreamscapist.valheim.inventorysort"); _harmony.PatchAll(); Log.LogInfo((object)"InventorySort 1.2.0 loaded"); } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = SortKey.Value; if (((KeyboardShortcut)(ref value)).IsDown() && InventorySorter.CanSortNow()) { Container currentContainer = InventoryGui.instance.m_currentContainer; if ((Object)(object)currentContainer != (Object)null) { InventorySorter.SortContainer(currentContainer); } else if (KeySortsPlayerWhenNoContainer.Value) { InventorySorter.SortPlayer(); } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } internal static class QuickStackLocks { private const string FileName = "dreamscapist.valheim.quickstackdeposit.locks.txt"; public static HashSet<(int, int)> LoadFor(Player player) { HashSet<(int, int)> hashSet = new HashSet<(int, int)>(); string path = Path.Combine(Paths.ConfigPath, "dreamscapist.valheim.quickstackdeposit.locks.txt"); if (!File.Exists(path)) { return hashSet; } string text = player.GetPlayerID().ToString(); try { string[] array = File.ReadAllLines(path); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); int num = text2.IndexOf('|'); if (num <= 0 || text2.Substring(0, num).Trim() != text) { continue; } string[] array2 = text2.Substring(num + 1).Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int j = 0; j < array2.Length; j++) { string[] array3 = array2[j].Split(','); if (array3.Length == 2 && int.TryParse(array3[0].Trim(), out var result) && int.TryParse(array3[1].Trim(), out var result2)) { hashSet.Add((result, result2)); } } } return hashSet; } catch (Exception ex) { InventorySortPlugin.Log.LogWarning((object)("Couldn't read QuickStackDeposit locks: " + ex.Message)); return hashSet; } } } [HarmonyPatch(typeof(InventoryGui), "Awake")] internal static class InventoryGui_Awake_Patch { private static void Postfix(InventoryGui __instance) { SortButtons.Create(__instance); } } internal static class SortButtons { [CompilerGenerated] private static class <>O { public static UnityAction <0>__SortPlayer; } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnityAction <>9__2_0; internal void <Create>b__2_0() { Container val = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null); if ((Object)(object)val != (Object)null) { InventorySorter.SortContainer(val); } } } private static Button _containerButton; private static Button _playerButton; public static void Create(InventoryGui gui) { //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) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_008a: 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) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown Button takeAllButton = gui.m_takeAllButton; if ((Object)(object)takeAllButton == (Object)null) { InventorySortPlugin.Log.LogWarning((object)"Take All button not found; Sort buttons not created"); return; } Rect rect = ((RectTransform)((Component)takeAllButton).transform).rect; Vector2 size = ((Rect)(ref rect)).size; _containerButton = Clone(takeAllButton, (Transform)(object)gui.m_container, "InventorySort_StorageSortButton", size); ButtonClickedEvent onClick = _containerButton.onClick; object obj = <>c.<>9__2_0; if (obj == null) { UnityAction val = delegate { Container val3 = (((Object)(object)InventoryGui.instance != (Object)null) ? InventoryGui.instance.m_currentContainer : null); if ((Object)(object)val3 != (Object)null) { InventorySorter.SortContainer(val3); } }; <>c.<>9__2_0 = val; obj = (object)val; } ((UnityEvent)onClick).AddListener((UnityAction)obj); _playerButton = Clone(takeAllButton, (Transform)(object)gui.m_player, "InventorySort_InventorySortButton", size); ButtonClickedEvent onClick2 = _playerButton.onClick; object obj2 = <>O.<0>__SortPlayer; if (obj2 == null) { UnityAction val2 = InventorySorter.SortPlayer; <>O.<0>__SortPlayer = val2; obj2 = (object)val2; } ((UnityEvent)onClick2).AddListener((UnityAction)obj2); ApplyLayout(); } public static void ApplyLayout() { Place(_containerButton, InventorySortPlugin.ContainerButtonX.Value, InventorySortPlugin.ContainerButtonY.Value); Place(_playerButton, InventorySortPlugin.PlayerButtonX.Value, InventorySortPlugin.PlayerButtonY.Value); } private static Button Clone(Button template, Transform parent, string name, Vector2 size) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, parent, false); ((Object)val).name = name; Button component = val.GetComponent<Button>(); component.onClick = new ButtonClickedEvent(); UIGamePad[] componentsInChildren = val.GetComponentsInChildren<UIGamePad>(true); foreach (UIGamePad val2 in componentsInChildren) { if ((Object)(object)val2.m_hint != (Object)null) { Object.Destroy((Object)(object)val2.m_hint); } Object.Destroy((Object)(object)val2); } TMP_Text componentInChildren = val.GetComponentInChildren<TMP_Text>(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "Sort"; } RectTransform val3 = (RectTransform)val.transform; val3.anchorMin = new Vector2(1f, 1f); val3.anchorMax = new Vector2(1f, 1f); val3.pivot = new Vector2(0f, 1f); val3.sizeDelta = size; return component; } private static void Place(Button button, float x, float y) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)button == (Object)null)) { ((RectTransform)((Component)button).transform).anchoredPosition = new Vector2(x, y); ((Component)button).gameObject.SetActive(InventorySortPlugin.ShowButtons.Value); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }